From 1befd4e5cdabd32b410795ffcce3d7b41b3efa57 Mon Sep 17 00:00:00 2001 From: vivet Date: Sat, 5 Sep 2026 17:14:34 +0200 Subject: [PATCH 1/6] Updated renaming of PostgresSqlProvider --- .github/workflows/build-and-deploy.yml | 2 +- .../Api.Data.PostgreSQL.Spatial/Program.cs | 2 +- Api.Data.PostgreSQL/Api.Data.PostgreSQL/Program.cs | 2 +- Console.Data.PostgreSQL/Console.Data.PostgreSQL/Program.cs | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build-and-deploy.yml b/.github/workflows/build-and-deploy.yml index f1131ccc..88e7ecf4 100644 --- a/.github/workflows/build-and-deploy.yml +++ b/.github/workflows/build-and-deploy.yml @@ -7,7 +7,7 @@ on: branches: - master env: - VERSION: 10.0.4 + VERSION: 10.0.5 jobs: build-and-deploy: runs-on: ubuntu-latest diff --git a/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/Program.cs b/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/Program.cs index 411bcfdc..d32a99a2 100644 --- a/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/Program.cs +++ b/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/Program.cs @@ -7,7 +7,7 @@ .ConfigureApp() .ConfigureServices(x => { - x.AddNanoData(); + x.AddNanoData(); }) .Build() .Run(); diff --git a/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Program.cs b/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Program.cs index 7c7e2ead..738f0ad2 100644 --- a/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Program.cs +++ b/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Program.cs @@ -7,7 +7,7 @@ .ConfigureApp() .ConfigureServices(x => { - x.AddNanoData(); + x.AddNanoData(); }) .Build() .Run(); diff --git a/Console.Data.PostgreSQL/Console.Data.PostgreSQL/Program.cs b/Console.Data.PostgreSQL/Console.Data.PostgreSQL/Program.cs index 23a3340f..21881272 100644 --- a/Console.Data.PostgreSQL/Console.Data.PostgreSQL/Program.cs +++ b/Console.Data.PostgreSQL/Console.Data.PostgreSQL/Program.cs @@ -7,7 +7,7 @@ .ConfigureApp(args) .ConfigureServices(x => { - x.AddNanoData(); + x.AddNanoData(); }) .Build() .Run(); \ No newline at end of file From d1e8c311eb5f210d6778d2cd91a2925e5e655662 Mon Sep 17 00:00:00 2001 From: vivet Date: Sun, 6 Sep 2026 12:46:38 +0200 Subject: [PATCH 2/6] Updated all readme's --- Api.ApiClients/README.md | 4 ++-- Api.Auth.External.Custom/README.md | 4 ++-- Api.Auth.RootLogin/README.md | 4 ++-- Api.Authorization/README.md | 5 +---- Api.Cookies/README.md | 2 +- Api.CustomService/README.md | 2 +- Api.Data.Audit/README.md | 4 ++-- Api.Data.EntityEvents/README.md | 2 +- .../appsettings.json | 2 +- .../appsettings.json | 2 +- .../appsettings.json | 2 +- .../Api.Data.Identity/appsettings.json | 2 +- Api.Data.Identity/README.md | 12 +++++------ Api.Data.InMemory/README.md | 2 +- Api.Data.LazyLoading/README.md | 2 +- .../Api.Data.MySql.Collation/appsettings.json | 1 + Api.Data.MySql.Mappings/README.md | 4 ++-- Api.Data.MySql.StoredProcedures/README.md | 2 +- Api.Data.MySql/README.md | 4 ++-- Api.Data.PostgreSQL/README.md | 4 ++-- Api.Data.SqLite/README.md | 6 +++--- Api.Data.SqlServer/README.md | 2 +- Api.Documentation.Csp/README.md | 4 ++-- .../Api.Documentation/appsettings.json | 3 +-- Api.Documentation/README.md | 3 +-- Api.Eventing.RabbitMq/README.md | 4 ++-- Api.Hosting.Https/README.md | 4 ++-- Api.Hosting.MultipartLimits/README.md | 18 ++++++++-------- Api.Localization/README.md | 2 +- Api.Metrics/README.md | 4 ++-- .../README.md | 21 ------------------- Api.PolicyHeaders.Cors/README.md | 2 +- Api.PolicyHeaders.ForwardedHeaders/README.md | 2 +- Api.PolicyHeaders.Hsts/README.md | 2 +- Api.PolicyHeaders.Robots/README.md | 4 ++-- Api.PolicyHeaders.XssProtection/README.md | 4 ++-- Api.Storage.Azure/README.md | 4 ++-- .../Api.Storage.Local/appsettings.json | 2 +- .../Controllers/ExamplesController.cs | 4 ++-- Api.VirusScan/Api.VirusScan/appsettings.json | 2 +- .../Console.Data.InMemory/appsettings.json | 5 +---- Console.Data.InMemory/README.md | 5 +---- .../appsettings.Development.json | 2 +- Console.Data.SqLite/README.md | 4 ++-- Console.Eventing.RabbitMq/README.md | 8 +++---- 45 files changed, 78 insertions(+), 109 deletions(-) diff --git a/Api.ApiClients/README.md b/Api.ApiClients/README.md index c1cec99d..abb03719 100644 --- a/Api.ApiClients/README.md +++ b/Api.ApiClients/README.md @@ -50,9 +50,9 @@ Configured the application with a connection to the `NanoApiClient`. "App": { "Apis": { "NanoApiClient": { - "Host": "localhost", + "Host": "api.apiclients.service", "Root": "api", - "Port": 8080, + "Port": 8181, "UseSsl": false, "Timeout": "00:00:30", "HealthCheck": { diff --git a/Api.Auth.External.Custom/README.md b/Api.Auth.External.Custom/README.md index 82cdea13..5a8b754a 100644 --- a/Api.Auth.External.Custom/README.md +++ b/Api.Auth.External.Custom/README.md @@ -14,7 +14,7 @@ Nano is referenced directly from source (not via NuGet packages) and is expected ## Table of Contents * [Summary](#summary) -* [Configuration](#summary) +* [Configuration](#configuration) * [Kubernetes](#kubernetes) * [GitHub Actions](#github-actions) @@ -44,7 +44,7 @@ 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`. | +| `http://localhost:8080/api/examples/authenticated` | 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/blob/master/Nano.App.Api/README.md#authentication)**. diff --git a/Api.Auth.RootLogin/README.md b/Api.Auth.RootLogin/README.md index 2ab1b9cd..6fb51814 100644 --- a/Api.Auth.RootLogin/README.md +++ b/Api.Auth.RootLogin/README.md @@ -14,7 +14,7 @@ Nano is referenced directly from source (not via NuGet packages) and is expected ## Table of Contents * [Summary](#summary) -* [Configuration](#summary) +* [Configuration](#configuration) * [Kubernetes](#kubernetes) * [GitHub Actions](#github-actions) @@ -40,7 +40,7 @@ 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`. | +| `http://localhost:8080/api/examples/authenticated` | 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/blob/master/Nano.App.Api/README.md#authentication)**. diff --git a/Api.Authorization/README.md b/Api.Authorization/README.md index 86595b84..fa3b8ba1 100644 --- a/Api.Authorization/README.md +++ b/Api.Authorization/README.md @@ -22,9 +22,6 @@ A custom authorization policy has been configured for the application. The `Serv 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**. @@ -40,7 +37,7 @@ 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/authenticated` | 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/blob/master/Nano.App.Api/README.md#authentication)**. diff --git a/Api.Cookies/README.md b/Api.Cookies/README.md index 9b01b482..5bf4455c 100644 --- a/Api.Cookies/README.md +++ b/Api.Cookies/README.md @@ -29,5 +29,5 @@ The following endpoint is available for testing: | `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/blob/master/Nano.App.Api#cookies)**. +> πŸ“– Learn more about **[Nano Cookies](https://github.com/Nano-Core/Nano.Library/blob/master/Nano.App.Api/README.md#cookies)**. diff --git a/Api.CustomService/README.md b/Api.CustomService/README.md index 0420a543..2bc06853 100644 --- a/Api.CustomService/README.md +++ b/Api.CustomService/README.md @@ -26,7 +26,7 @@ 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` | +| `http://localhost:8080/api/examples/custom-service` | Returns a simple `200 OK` response, with a message from the `IExampleService` | > πŸ“– Learn more about **[Nano Custom Services](https://github.com/Nano-Core/Nano.Library/blob/master/Nano.App/README.md#custom-services)**. diff --git a/Api.Data.Audit/README.md b/Api.Data.Audit/README.md index 0e86567a..f12a4054 100644 --- a/Api.Data.Audit/README.md +++ b/Api.Data.Audit/README.md @@ -17,8 +17,8 @@ Nano is referenced directly from source (not via NuGet packages) and is expected ## Summary This application builds on **[Api.Data.MySql](https://github.com/Nano-Core/Nano.Lessons/blob/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. +demonstrate audit logging. Entity controllers have been simplified to showcase autosave; full controllers are unnecessary. Also an `AuditController` derived from +`BaseAuditController` 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`. diff --git a/Api.Data.EntityEvents/README.md b/Api.Data.EntityEvents/README.md index d8bed3f3..dfbd2dd1 100644 --- a/Api.Data.EntityEvents/README.md +++ b/Api.Data.EntityEvents/README.md @@ -17,7 +17,7 @@ Nano is referenced directly from source (not via NuGet packages) and is expected ## Summary This application builds on **[Api.Data.MySql](https://github.com/Nano-Core/Nano.Lessons/blob/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.RabbitMq)**, +demonstrate entity events. Additionally, eventing has been enabled mirroring the setup from **[Api.Eventing.RabbitMq](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api.Eventing.RabbitMq)**, 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 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 index 719ba02e..b33c1d79 100644 --- 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 @@ -48,7 +48,7 @@ "RequireDigit": true, "RequireNonAlphanumeric": true, "RequireLowercase": true, - "RequirUppercase": true, + "RequireUppercase": true, "RequiredLength": 12, "RequiredUniqueCharacters": 3 }, 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 index 2494e2d7..1e60b60d 100644 --- 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 @@ -48,7 +48,7 @@ "RequireDigit": true, "RequireNonAlphanumeric": true, "RequireLowercase": true, - "RequirUppercase": true, + "RequireUppercase": true, "RequiredLength": 12, "RequiredUniqueCharacters": 3 } 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 index 2494e2d7..1e60b60d 100644 --- 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 @@ -48,7 +48,7 @@ "RequireDigit": true, "RequireNonAlphanumeric": true, "RequireLowercase": true, - "RequirUppercase": true, + "RequireUppercase": true, "RequiredLength": 12, "RequiredUniqueCharacters": 3 } diff --git a/Api.Data.Identity/Api.Data.Identity/appsettings.json b/Api.Data.Identity/Api.Data.Identity/appsettings.json index 1e2f21b9..f672c64e 100644 --- a/Api.Data.Identity/Api.Data.Identity/appsettings.json +++ b/Api.Data.Identity/Api.Data.Identity/appsettings.json @@ -38,7 +38,7 @@ "RequireDigit": true, "RequireNonAlphanumeric": true, "RequireLowercase": true, - "RequirUppercase": true, + "RequireUppercase": true, "RequiredLength": 12, "RequiredUniqueCharacters": 3 } diff --git a/Api.Data.Identity/README.md b/Api.Data.Identity/README.md index bb4e8017..04fd3bac 100644 --- a/Api.Data.Identity/README.md +++ b/Api.Data.Identity/README.md @@ -18,10 +18,10 @@ Nano is referenced directly from source (not via NuGet packages) and is expected ## Summary This application builds on **[Api.Data.MySql](https://github.com/Nano-Core/Nano.Lessons/blob/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. +demonstrate data identity. 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 `User` entity model, `UserMapping` data mapping, `UserQueryCriteria` query criteria and the `UsersController` has been added to the solution. The controller is deriving +from the `BaseEntityUserController`, exposing all configured identity actions. Everything needed to expose identity controller actions. The application is configured to audit all identity models. @@ -36,8 +36,8 @@ authentication methods and the corresponding identity actions to manage them. > πŸ“– Learn more about **[Nano Data Identity](https://github.com/Nano-Core/Nano.Library/blob/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. +The data identity has been configured for the application. The `UseAudit` has 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 identity model to audit. ```json "Data": { @@ -66,7 +66,7 @@ selective in that option and choose some identityt model to audit. "RequireDigit": true, "RequireNonAlphanumeric": true, "RequireLowercase": true, - "RequirUppercase": true, + "RequireUppercase": true, "RequiredLength": 12, "RequiredUniqueCharacters": 3 } diff --git a/Api.Data.InMemory/README.md b/Api.Data.InMemory/README.md index 5430295c..ebd53315 100644 --- a/Api.Data.InMemory/README.md +++ b/Api.Data.InMemory/README.md @@ -19,7 +19,7 @@ Nano is referenced directly from source (not via NuGet packages) and is expected ## Summary This application builds on **[Api.Blank](https://github.com/Nano-Core/Nano.Lessons/blob/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. +the Nano `BaseEntityController`. 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/blob/master/Nano.Data/README.md#data-models), [Data Mappings](https://github.com/Nano-Core/Nano.Library/blob/master/Nano.Data/README.md#data-mappings), diff --git a/Api.Data.LazyLoading/README.md b/Api.Data.LazyLoading/README.md index ef28c355..50c938c6 100644 --- a/Api.Data.LazyLoading/README.md +++ b/Api.Data.LazyLoading/README.md @@ -18,7 +18,7 @@ Nano is referenced directly from source (not via NuGet packages) and is expected ## Summary This application builds on **[Api.Data.MySql](https://github.com/Nano-Core/Nano.Lessons/blob/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. +demonstrate lazy loading. Entity controllers have been simplified to showcase lazy loading; 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. diff --git a/Api.Data.MySql.Collation/Api.Data.MySql.Collation/appsettings.json b/Api.Data.MySql.Collation/Api.Data.MySql.Collation/appsettings.json index d43a066a..16c4f89a 100644 --- a/Api.Data.MySql.Collation/Api.Data.MySql.Collation/appsettings.json +++ b/Api.Data.MySql.Collation/Api.Data.MySql.Collation/appsettings.json @@ -11,6 +11,7 @@ } }, "Data": { + "DefaultCollation": "utf8mb4_general_ci", "ConnectionString": null } } \ No newline at end of file diff --git a/Api.Data.MySql.Mappings/README.md b/Api.Data.MySql.Mappings/README.md index ad2a0144..73767c0e 100644 --- a/Api.Data.MySql.Mappings/README.md +++ b/Api.Data.MySql.Mappings/README.md @@ -17,7 +17,7 @@ Nano is referenced directly from source (not via NuGet packages) and is expected ## Summary This application builds on **[Api.Data.MySql](https://github.com/Nano-Core/Nano.Lessons/blob/master/Api.Data.MySql)**. Entity controllers have been -simplified to showcase setting mysql views; full controllers are unnecessary. +simplified to showcase advanced data mappings; 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`, @@ -25,6 +25,6 @@ also has a `Profile` reference like `ExampleJson`, but the complex object is sto 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_'. +Last, a unique index has also been added to `ExampleNormalized.FullNameNormalized`. Observe how Nano renames the index prefixing with 'UX_'. > πŸ“– Learn more about **[Nano.Data.MySql](https://github.com/Nano-Core/Nano.Library/blob/master/Nano.Data.MySql/README.md#nanodatamysql)**. diff --git a/Api.Data.MySql.StoredProcedures/README.md b/Api.Data.MySql.StoredProcedures/README.md index 8c58118b..381edba8 100644 --- a/Api.Data.MySql.StoredProcedures/README.md +++ b/Api.Data.MySql.StoredProcedures/README.md @@ -26,7 +26,7 @@ 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 +The stored procedure is executed using Nano’s `IRepository.ExecuteProcedureAsync(...)` 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: diff --git a/Api.Data.MySql/README.md b/Api.Data.MySql/README.md index abcc8913..13829ad8 100644 --- a/Api.Data.MySql/README.md +++ b/Api.Data.MySql/README.md @@ -22,7 +22,7 @@ Nano is referenced directly from source (not via NuGet packages) and is expected ## Summary This application builds on **[Api.Blank](https://github.com/Nano-Core/Nano.Lessons/blob/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. +the Nano `BaseEntityController`. 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/blob/master/Nano.Data/README.md#data-models)**, **[Data Mappings](https://github.com/Nano-Core/Nano.Library/blob/master/Nano.Data/README.md#data-mappings)**, @@ -98,7 +98,7 @@ Configured the application with the necessary data setup. ```json "Data": { "StartupAction": "Migrate", - "ConnectionString": "Server=host.docker.internal;Database=nanoDb;Uid=sa;Pwd=myPassword_123" + "ConnectionString": "Server=host.docker.internal;Database=nanoDb;Uid=root;Pwd=myPassword_123" } ``` diff --git a/Api.Data.PostgreSQL/README.md b/Api.Data.PostgreSQL/README.md index 8e6bc1c7..8711e020 100644 --- a/Api.Data.PostgreSQL/README.md +++ b/Api.Data.PostgreSQL/README.md @@ -22,7 +22,7 @@ Nano is referenced directly from source (not via NuGet packages) and is expected ## Summary This application builds on **[Api.Blank](https://github.com/Nano-Core/Nano.Lessons/blob/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. +the Nano `BaseEntityController`. 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/blob/master/Nano.Data/README.md#data-models)**, **[Data Mappings](https://github.com/Nano-Core/Nano.Library/blob/master/Nano.Data/README.md#data-mappings)**, @@ -44,7 +44,7 @@ here: **[http://localhost:8080/docs](http://localhost:8080/docs)**. 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/blob/master/Nano.Data.PostgreSQL/README.md#nanodatapostgresqll)**. +> πŸ“– Learn more about **[Nano.Data.PostgreSQL](https://github.com/Nano-Core/Nano.Library/blob/master/Nano.Data.PostgreSQL/README.md#nanodatapostgresql)**. ## Registration The following data provider has been registered using `ConfigureServices(...)` in `program.cs`. diff --git a/Api.Data.SqLite/README.md b/Api.Data.SqLite/README.md index b6e65ebb..b5e2461e 100644 --- a/Api.Data.SqLite/README.md +++ b/Api.Data.SqLite/README.md @@ -22,7 +22,7 @@ Nano is referenced directly from source (not via NuGet packages) and is expected ## Summary This application builds on **[Api.Blank](https://github.com/Nano-Core/Nano.Lessons/blob/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. +the Nano `BaseEntityController`. 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/blob/master/Nano.Data/README.md#data-models), [Data Mappings](https://github.com/Nano-Core/Nano.Library/blob/master/Nano.Data/README.md#data-mappings), @@ -75,11 +75,11 @@ Configured the application with the necessary data setup. "BulkBatchDelay": 1000, "QueryRetryCount": 0, "UseLazyLoading": false, - "StartupAction": "None", + "StartupAction": "Migrate", "UseSensitiveDataLogging": false, "QuerySplittingBehavior": "SingleQuery", "DefaultCollation": null, - "ConnectionString": "Data Source=/data/nanoDb.sqlite", + "ConnectionString": "Data Source=/mnt/data/nanoDb.sqlite", "Repository": { "UseAutoSave": true, "QueryIncludeDepth": 4 diff --git a/Api.Data.SqlServer/README.md b/Api.Data.SqlServer/README.md index 00ce9a53..73748cf1 100644 --- a/Api.Data.SqlServer/README.md +++ b/Api.Data.SqlServer/README.md @@ -22,7 +22,7 @@ Nano is referenced directly from source (not via NuGet packages) and is expected ## Summary This application builds on **[Api.Blank](https://github.com/Nano-Core/Nano.Lessons/blob/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. +the Nano `BaseEntityController`. 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/blob/master/Nano.Data/README.md#data-models)**, **[Data Mappings](https://github.com/Nano-Core/Nano.Library/blob/master/Nano.Data/README.md#data-mappings)**, diff --git a/Api.Documentation.Csp/README.md b/Api.Documentation.Csp/README.md index 61278b81..305eebe3 100644 --- a/Api.Documentation.Csp/README.md +++ b/Api.Documentation.Csp/README.md @@ -17,9 +17,9 @@ Nano is referenced directly from source (not via NuGet packages) and is expected * [Configuration](#configuration) ## Summary -This application builds on **[Api.Documenation](https://github.com/Nano-Core/Nano.Lessons/blob/master/Api.Documenation)**. +This application builds on **[Api.Documentation](https://github.com/Nano-Core/Nano.Lessons/blob/master/Api.Documentation)**. -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 +This example shows using API documentation with strict CSP security. Run the solution and open [http://localhost:8080/docs](http://localhost:8080/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. diff --git a/Api.Documentation/Api.Documentation/appsettings.json b/Api.Documentation/Api.Documentation/appsettings.json index 135418df..7c3b1e43 100644 --- a/Api.Documentation/Api.Documentation/appsettings.json +++ b/Api.Documentation/Api.Documentation/appsettings.json @@ -12,7 +12,7 @@ "Documentation": { "Name": "Application", "Description": "This is an example application", - "TermsOfService": "https://github.com/Nano-Core/Nano.Library/blob/master/LICENSE", + "TermsOfServiceUrl": "https://github.com/Nano-Core/Nano.Library/blob/master/LICENSE", "Contact": { "Name": "Nano Contributors", "Email": "email@email.com", @@ -23,7 +23,6 @@ "Identifier": "MIT", "Url": "https://github.com/Nano-Core/Nano.Library/blob/master/LICENSE" }, - "CspNonce": null, "HideDefaultVersion": true } } diff --git a/Api.Documentation/README.md b/Api.Documentation/README.md index bff4447e..b9b980a6 100644 --- a/Api.Documentation/README.md +++ b/Api.Documentation/README.md @@ -40,7 +40,7 @@ for the same example endpoint. When set to `true`, Swagger only displays the non "Documentation": { "Name": "Application", "Description": "This is an example application", - "TermsOfService": "https://github.com/Nano-Core/Nano.Library/blob/master/LICENSE", + "TermsOfServiceUrl": "https://github.com/Nano-Core/Nano.Library/blob/master/LICENSE", "Contact": { "Name": "Nano Contributors", "Email": "email@email.com", @@ -51,7 +51,6 @@ for the same example endpoint. When set to `true`, Swagger only displays the non "Identifier": "MIT", "Url": "https://github.com/Nano-Core/Nano.Library/blob/master/LICENSE" }, - "CspNonce": "null, "HideDefaultVersion": true } } diff --git a/Api.Eventing.RabbitMq/README.md b/Api.Eventing.RabbitMq/README.md index d5ff6c68..89445300 100644 --- a/Api.Eventing.RabbitMq/README.md +++ b/Api.Eventing.RabbitMq/README.md @@ -135,12 +135,12 @@ spec: spec: containers: env: - - name: Eventing__Credentials__Host + - name: Eventing__Host valueFrom: secretKeyRef: name: rabbitmq-default-user key: host - - name: Eventing__Credentials__Port + - name: Eventing__Port valueFrom: secretKeyRef: name: rabbitmq-default-user diff --git a/Api.Hosting.Https/README.md b/Api.Hosting.Https/README.md index 3d027dba..76f3500f 100644 --- a/Api.Hosting.Https/README.md +++ b/Api.Hosting.Https/README.md @@ -32,7 +32,7 @@ The following endpoints are available for testing: | Endpoint | Description | | -------------------------------------------- | -------------------------------------- | -| `http://localhost:8080/api/examples/http` | Redirects to HTTPS. | +| `http://localhost:8080/api/examples/https` | 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/blob/master/Nano.App.Api/README.md#https)**. @@ -64,7 +64,7 @@ and the `appsettings.Development.json` this. ```json "App": { "Hosting": { - "http": { + "Http": { "UseHttpsRedirection": true }, "Https": { diff --git a/Api.Hosting.MultipartLimits/README.md b/Api.Hosting.MultipartLimits/README.md index d5e4fdee..67cfa18d 100644 --- a/Api.Hosting.MultipartLimits/README.md +++ b/Api.Hosting.MultipartLimits/README.md @@ -1,6 +1,6 @@ ο»Ώ# Api.Hosting.MultipartLimits -> Nano API application with upload limits._ +> _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. @@ -30,18 +30,18 @@ The following endpoint is available for testing. | ------------------------------------------------- | -------------------------------------------------------- | | `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/blob/master/Nano.App.Api#multipart-limits)**. +> πŸ“– Learn more about **[Nano MultiPart Limits](https://github.com/Nano-Core/Nano.Library/blob/master/Nano.App.Api/README.md#multipart-limits)**. ## Configuration Added the following configuration to `appsettings.json`. ```json -Added - "App": { - "Hosting": { - "MultipartLimits": { - "MaxUploadBytes": 1048576, - "KeepAliveTimeout": 30 - } +"App": { + "Hosting": { + "MultipartLimits": { + "MaxUploadBytes": 1048576, + "KeepAliveTimeout": 30 + } + } } ``` diff --git a/Api.Localization/README.md b/Api.Localization/README.md index 12d883cc..c5fdf1a3 100644 --- a/Api.Localization/README.md +++ b/Api.Localization/README.md @@ -28,7 +28,7 @@ The controller return a response in the following format: { "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 + "NativeName": "Dansk" // The native name of the culture language } ``` diff --git a/Api.Metrics/README.md b/Api.Metrics/README.md index e2cea0b3..a1f97860 100644 --- a/Api.Metrics/README.md +++ b/Api.Metrics/README.md @@ -25,9 +25,9 @@ This example demonstrates how OpenTelemetry can be configured to expose metrics | Endpoint | Description | | ------------------------------------ | ------------------------------------------------ | -| `http://localhost:8080/api/metrics` | Returns the current metrics for the application. | +| `http://localhost:8080/metrics` | Returns the current metrics for the application. | -> πŸ“– Learn more about **[Nano OpenTelemetry Metrics](https://github.com/Nano-Core/Nano.Library/blob/master/Nano.App.Api/README.md#opentelemetry-metrics)**. +> πŸ“– Learn more about **[Nano OpenTelemetry Metrics](https://github.com/Nano-Core/Nano.Library/blob/master/Nano.App.Api/README.md#metrics-opentelemetry)**. ## Configuration Added the empty `Metrics` section to enable OpenTelemetry with Prometheus for the application. diff --git a/Api.PolicyHeaders.ContentSecurityPolicy/README.md b/Api.PolicyHeaders.ContentSecurityPolicy/README.md index 5cbb37fd..7b33a961 100644 --- a/Api.PolicyHeaders.ContentSecurityPolicy/README.md +++ b/Api.PolicyHeaders.ContentSecurityPolicy/README.md @@ -89,27 +89,6 @@ Added content security policy header configuration. "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, diff --git a/Api.PolicyHeaders.Cors/README.md b/Api.PolicyHeaders.Cors/README.md index 689c361a..5cac1b82 100644 --- a/Api.PolicyHeaders.Cors/README.md +++ b/Api.PolicyHeaders.Cors/README.md @@ -44,7 +44,7 @@ Also try out the endpoint, and observe how CORS returns the allowed hosts, heade "POST", "OPTIONS" ], - "AllowCredentials": true, + "AllowCredentials": false, "Origin": { "EmbedderPolicy": "UnsafeNone", "OpenerPolicy": "SameOriginAllowPopups", diff --git a/Api.PolicyHeaders.ForwardedHeaders/README.md b/Api.PolicyHeaders.ForwardedHeaders/README.md index c32d9b9e..4bddaedf 100644 --- a/Api.PolicyHeaders.ForwardedHeaders/README.md +++ b/Api.PolicyHeaders.ForwardedHeaders/README.md @@ -25,7 +25,7 @@ which reflect the internal service values rather than the forwarded ones. The ex | Endpoint | Description | | -------------------------------------------- | ------------------------------------------------------------------------ | -| `http://localhost:8080/api/examples/nosniff` | Returns a `200 OK` response with `HttpContext` forwarded header values. | +| `http://localhost:8080/api/examples/forwarded-headers` | Returns a `200 OK` response with `HttpContext` forwarded header values. | > πŸ“– Learn more about **[Nano Forwarded Headers](https://github.com/Nano-Core/Nano.Library/blob/master/Nano.App.Api/README.md#forwarded-headers)**. diff --git a/Api.PolicyHeaders.Hsts/README.md b/Api.PolicyHeaders.Hsts/README.md index 50e7c518..3cb8aad5 100644 --- a/Api.PolicyHeaders.Hsts/README.md +++ b/Api.PolicyHeaders.Hsts/README.md @@ -25,7 +25,7 @@ To observe HSTS enforcement in action, load the `hsts-violation.html` file and s | Endpoint | Description | | ----------------------------------------- | --------------------------------------------------------------------------------------- | -| `http://localhost:8080/api/examples/hsts` | Returns a `200 OK` response including the `Strict-Transform-Security` response header. | +| `http://localhost:8080/api/examples/hsts` | Returns a `200 OK` response including the `Strict-Transport-Security` response header. | > πŸ“– Learn more about **[Nano Strict Transport Security (HSTS)](https://github.com/Nano-Core/Nano.Library/blob/master/Nano.App.Api/README.md#strict-transport-security-hsts)**. diff --git a/Api.PolicyHeaders.Robots/README.md b/Api.PolicyHeaders.Robots/README.md index 7cbdf248..4e762ce7 100644 --- a/Api.PolicyHeaders.Robots/README.md +++ b/Api.PolicyHeaders.Robots/README.md @@ -26,9 +26,9 @@ 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. | +| `http://localhost:8080/api/examples/robots` | 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/blob/master/Nano.App.Api/README.md#robots)**. +> πŸ“– Learn more about **[Nano Robots Header](https://github.com/Nano-Core/Nano.Library/blob/master/Nano.App.Api/README.md#robots)**. ## Configuration ```json diff --git a/Api.PolicyHeaders.XssProtection/README.md b/Api.PolicyHeaders.XssProtection/README.md index 4140c31b..ea1e05e1 100644 --- a/Api.PolicyHeaders.XssProtection/README.md +++ b/Api.PolicyHeaders.XssProtection/README.md @@ -1,6 +1,6 @@ ο»Ώ# Api.PolicyHeaders.XssProtection -> _Nano API application with xxs protection._ +> _Nano API application with xss 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. @@ -35,7 +35,7 @@ The following endpoint is available for testing. | ---------------------------------------- | ------------------------------------------------------------------------------ | | `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/blob/master/Nano.App.Api/README.md#xss-protection)**. +> πŸ“– Learn more about **[Nano Xss Protection Header](https://github.com/Nano-Core/Nano.Library/blob/master/Nano.App.Api/README.md#xss-protection)**. ## Configuration ```json diff --git a/Api.Storage.Azure/README.md b/Api.Storage.Azure/README.md index 7331c81c..d92986b7 100644 --- a/Api.Storage.Azure/README.md +++ b/Api.Storage.Azure/README.md @@ -30,8 +30,8 @@ When running locally, files are **NOT** written to the Azure File Share. Instead 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`. +A storage health check is configured to target the Azure File Share, using the connection information already configured for the storage provider. If it is unreachable, +the health check will report `Unhealthy`. Open [http://localhost:8080/healthz](http://localhost:8080/healthz) to view the storage health-check JSON response. diff --git a/Api.Storage.Local/Api.Storage.Local/appsettings.json b/Api.Storage.Local/Api.Storage.Local/appsettings.json index 6a2c9c46..4f350444 100644 --- a/Api.Storage.Local/Api.Storage.Local/appsettings.json +++ b/Api.Storage.Local/Api.Storage.Local/appsettings.json @@ -15,7 +15,7 @@ "Storage": { "ShareName": "nano-storage-local", "HealthCheck": { - "UnhealthyStatus": "UnHealthy" + "UnhealthyStatus": "Unhealthy" } } } \ No newline at end of file diff --git a/Api.TimeZone/Api.TimeZone/Controllers/ExamplesController.cs b/Api.TimeZone/Api.TimeZone/Controllers/ExamplesController.cs index f41edbdb..0b0f4949 100644 --- a/Api.TimeZone/Api.TimeZone/Controllers/ExamplesController.cs +++ b/Api.TimeZone/Api.TimeZone/Controllers/ExamplesController.cs @@ -36,7 +36,7 @@ public virtual async Task TimeZoneAsync([FromQuery][Required]Date return this.Ok(new { RequestDateTimeLocal = clientRequestDateTime, - ServerRecievedUtc = dateTime.UtcDateTime, + ServerReceivedUtc = dateTime.UtcDateTime, ResponseDateTimeLocal = dateTime, DateTimeInfoNow = DateTimeInfo.Now, DateTimeInfoUtcNow = DateTimeInfo.UtcNow.UtcDateTime @@ -62,7 +62,7 @@ public virtual async Task TimeZoneAsync([FromBody][Required]DateT return this.Ok(new { RequestDateTimeLocal = clientRequestDateTime, - ServerRecievedUtc = request.DateTime.UtcDateTime, + ServerReceivedUtc = request.DateTime.UtcDateTime, ResponseDateTimeLocal = request.DateTime, DateTimeInfoNow = DateTimeInfo.Now, DateTimeInfoUtcNow = DateTimeInfo.UtcNow.UtcDateTime diff --git a/Api.VirusScan/Api.VirusScan/appsettings.json b/Api.VirusScan/Api.VirusScan/appsettings.json index e3e6bc45..f77de8f9 100644 --- a/Api.VirusScan/Api.VirusScan/appsettings.json +++ b/Api.VirusScan/Api.VirusScan/appsettings.json @@ -7,7 +7,7 @@ "Ports": [ 8080 ] - }, + } }, "HealthCheck": { }, diff --git a/Console.Data.InMemory/Console.Data.InMemory/appsettings.json b/Console.Data.InMemory/Console.Data.InMemory/appsettings.json index e0008651..dec4c00b 100644 --- a/Console.Data.InMemory/Console.Data.InMemory/appsettings.json +++ b/Console.Data.InMemory/Console.Data.InMemory/appsettings.json @@ -8,11 +8,8 @@ "BulkBatchDelay": 1000, "QueryRetryCount": 0, "UseLazyLoading": false, - "UseCreateDatabase": false, - "UseMigrateDatabase": false, - "UseSoftDeletetion": false, + "StartupAction": "None", "UseSensitiveDataLogging": false, - "UseAudit": false, "QuerySplittingBehavior": "SingleQuery", "DefaultCollation": null, "ConnectionString": "nanoDb", diff --git a/Console.Data.InMemory/README.md b/Console.Data.InMemory/README.md index 01dd5519..08f6f8e0 100644 --- a/Console.Data.InMemory/README.md +++ b/Console.Data.InMemory/README.md @@ -52,11 +52,8 @@ Configured the application with the necessary data setup. "BulkBatchDelay": 1000, "QueryRetryCount": 0, "UseLazyLoading": false, - "UseCreateDatabase": false, - "UseMigrateDatabase": false, - "UseSoftDeletetion": false, + "StartupAction": "None", "UseSensitiveDataLogging": false, - "UseAudit": false, "QuerySplittingBehavior": "SingleQuery", "DefaultCollation": null, "ConnectionString": "nanoDb", diff --git a/Console.Data.MySql/Console.Data.MySql/appsettings.Development.json b/Console.Data.MySql/Console.Data.MySql/appsettings.Development.json index 670c9b68..34379ce9 100644 --- a/Console.Data.MySql/Console.Data.MySql/appsettings.Development.json +++ b/Console.Data.MySql/Console.Data.MySql/appsettings.Development.json @@ -1,6 +1,6 @@ ο»Ώ{ "Data": { "StartupAction": "Migrate", - "ConnectionString": "Server=host.docker.internal;Database=nanoDb;Uid=root;Pwd=myPassword_123" + "ConnectionString": "Server=host.docker.internal;Database=nanoDb;Uid=sa;Pwd=myPassword_123" } } \ No newline at end of file diff --git a/Console.Data.SqLite/README.md b/Console.Data.SqLite/README.md index 88147434..2fe99a5c 100644 --- a/Console.Data.SqLite/README.md +++ b/Console.Data.SqLite/README.md @@ -58,12 +58,12 @@ Configured the application with the necessary data setup. "BulkBatchSize": 500, "BulkBatchDelay": 1000, "QueryRetryCount": 0, - "StartupAction": "None", + "StartupAction": "Migrate", "UseLazyLoading": false, "UseSensitiveDataLogging": false, "QuerySplittingBehavior": "SingleQuery", "DefaultCollation": null, - "ConnectionString": "Data Source=/data/nanoDb.sqlite", + "ConnectionString": "Data Source=/mnt/data/nanoDb.sqlite", "Repository": { "UseAutoSave": true, "QueryIncludeDepth": 4 diff --git a/Console.Eventing.RabbitMq/README.md b/Console.Eventing.RabbitMq/README.md index f46e3815..35a209a1 100644 --- a/Console.Eventing.RabbitMq/README.md +++ b/Console.Eventing.RabbitMq/README.md @@ -59,8 +59,8 @@ Configured the application `appsettings.json` with the necessary eventing setup. "Heartbeat": 60, "PrefetchCount": 50, "Credentials": { - "Id": "rabbitmq_user", - "Secret": "password" + "Id": null, + "Secret": null } } ``` @@ -112,12 +112,12 @@ spec: spec: containers: env: - - name: Eventing__Credentials__Host + - name: Eventing__Host valueFrom: secretKeyRef: name: rabbitmq-default-user key: host - - name: Eventing__Credentials__Port + - name: Eventing__Port valueFrom: secretKeyRef: name: rabbitmq-default-user From eab3855867a6802474e8a9fefc22eac122099cc6 Mon Sep 17 00:00:00 2001 From: vivet Date: Sun, 6 Sep 2026 13:29:37 +0200 Subject: [PATCH 3/6] Fixed bugs with Claude --- .../Dockerfile.Local | 2 +- Api.Data.EntityEvents/.docker/docker-compose.yml | 2 +- .../.kubernetes/deployment.yaml | 16 +++++++++++++--- .../.kubernetes/deployment.yaml | 2 +- Api.Data.SoftDelete/.docker/docker-compose.yml | 4 ++-- Api.Data.SqLite/.kubernetes/data-pvc.yaml | 1 + Api.Data.SqLite/.kubernetes/deployment.yaml | 2 +- Api.Data.Triggers/.docker/docker-compose.yml | 4 ++-- .../.github/workflows/build-and-deploy.yml | 2 +- .../.github/workflows/build-and-deploy.yml | 1 + .../.github/workflows/build-and-deploy.yml | 2 +- .../.kubernetes/deployment.yaml | 4 ++-- .../.github/workflows/build-and-deploy.yml | 1 + .../.github/workflows/build-and-deploy.yml | 2 +- Api.Metrics/.kubernetes/service-monitor.yaml | 2 +- Api.Metrics/.kubernetes/service.yaml | 2 ++ Api.Metrics/README.md | 2 +- Api.Storage.Azure/.kubernetes/deployment.yaml | 5 ----- Api.Storage.Azure/.kubernetes/storage-pv.yaml | 2 +- Api.Storage.Azure/.kubernetes/storage-pvc.yaml | 2 +- .../.github/workflows/build-and-deploy.yml | 2 +- .../.github/workflows/build-and-deploy.yml | 1 + .../.github/workflows/build-and-deploy.yml | 1 - .../.github/workflows/build-and-deploy.yml | 4 ++-- Console.Data.SqLite/.kubernetes/cronjob.yaml | 2 +- .../.kubernetes/cronjob.yaml | 4 ++-- .../.kubernetes/storage-pv.yaml | 2 +- .../.kubernetes/storage-pvc.yaml | 2 +- .../.github/workflows/build-and-deploy.yml | 2 +- Console.Storage.Local/.kubernetes/cronjob.yaml | 1 + 30 files changed, 46 insertions(+), 35 deletions(-) diff --git a/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service/Dockerfile.Local b/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service/Dockerfile.Local index 18c597d8..ba2a9eed 100644 --- a/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service/Dockerfile.Local +++ b/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service/Dockerfile.Local @@ -5,4 +5,4 @@ EXPOSE 8080 ENV ASPNETCORE_HTTP_PORTS= -ENTRYPOINT ["dotnet", "Api.Auth.RootLogin.Service.dll"] \ No newline at end of file +ENTRYPOINT ["dotnet", "Api.ApiClients.RootLogin.Service.dll"] \ No newline at end of file diff --git a/Api.Data.EntityEvents/.docker/docker-compose.yml b/Api.Data.EntityEvents/.docker/docker-compose.yml index de7e7113..e0ad465e 100644 --- a/Api.Data.EntityEvents/.docker/docker-compose.yml +++ b/Api.Data.EntityEvents/.docker/docker-compose.yml @@ -19,7 +19,7 @@ services: hostname: api-data-entityevents.subscriber restart: on-failure ports: - - 8181:8181 + - 8181:8080 build: context: ../Api.Data.EntityEvents.Subscriber dockerfile: "Dockerfile.Local" diff --git a/Api.Data.EntityEvents/.kubernetes/deployment.yaml b/Api.Data.EntityEvents/.kubernetes/deployment.yaml index 5181e537..035669ed 100644 --- a/Api.Data.EntityEvents/.kubernetes/deployment.yaml +++ b/Api.Data.EntityEvents/.kubernetes/deployment.yaml @@ -43,17 +43,27 @@ spec: secretKeyRef: name: %SERVICE_NAME%-sql-auth-secret key: data-connectionstring + - name: Eventing__Host + valueFrom: + secretKeyRef: + name: rabbitmq-default-user + key: host + - name: Eventing__Port + valueFrom: + secretKeyRef: + name: rabbitmq-default-user + key: port - name: Eventing__Credentials__Id valueFrom: secretKeyRef: - name: rabbitmq-auth + name: rabbitmq-default-user key: username - name: Eventing__Credentials__Secret valueFrom: secretKeyRef: - name: rabbitmq-auth + name: rabbitmq-default-user key: password - envFrom: + envFrom: - configMapRef: name: %SERVICE_NAME%-config resources: diff --git a/Api.Data.Identity.Auth.ApiKey/.kubernetes/deployment.yaml b/Api.Data.Identity.Auth.ApiKey/.kubernetes/deployment.yaml index 3824a48e..377e6026 100644 --- a/Api.Data.Identity.Auth.ApiKey/.kubernetes/deployment.yaml +++ b/Api.Data.Identity.Auth.ApiKey/.kubernetes/deployment.yaml @@ -43,7 +43,7 @@ spec: secretKeyRef: name: %SERVICE_NAME%-sql-auth-secret key: data-connectionstring - - name: Data__Identity__ApiKey + - name: Data__Identity__ApiKey__Secret valueFrom: secretKeyRef: name: auth-api-key-secret diff --git a/Api.Data.SoftDelete/.docker/docker-compose.yml b/Api.Data.SoftDelete/.docker/docker-compose.yml index 0fbddac4..bf83a3c8 100644 --- a/Api.Data.SoftDelete/.docker/docker-compose.yml +++ b/Api.Data.SoftDelete/.docker/docker-compose.yml @@ -1,6 +1,6 @@ services: - api.data.repository.softdelete: - image: api.data.repository.softdelete + api.data.softdelete: + image: api.data.softdelete hostname: api-data-softdelete restart: on-failure ports: diff --git a/Api.Data.SqLite/.kubernetes/data-pvc.yaml b/Api.Data.SqLite/.kubernetes/data-pvc.yaml index 00bca640..f3a52c3c 100644 --- a/Api.Data.SqLite/.kubernetes/data-pvc.yaml +++ b/Api.Data.SqLite/.kubernetes/data-pvc.yaml @@ -2,6 +2,7 @@ apiVersion: v1 kind: PersistentVolumeClaim metadata: name: %SERVICE_NAME%-data-pvc + namespace: %KUBERNETES_NAMESPACE% spec: accessModes: - ReadWriteOnce diff --git a/Api.Data.SqLite/.kubernetes/deployment.yaml b/Api.Data.SqLite/.kubernetes/deployment.yaml index fd8b1951..9c1bb835 100644 --- a/Api.Data.SqLite/.kubernetes/deployment.yaml +++ b/Api.Data.SqLite/.kubernetes/deployment.yaml @@ -79,4 +79,4 @@ spec: volumes: - name: %SERVICE_NAME%-volume persistentVolumeClaim: - claimName: %SERVICE_NAME%-pvc + claimName: %SERVICE_NAME%-data-pvc diff --git a/Api.Data.Triggers/.docker/docker-compose.yml b/Api.Data.Triggers/.docker/docker-compose.yml index b7e371df..3fd7950a 100644 --- a/Api.Data.Triggers/.docker/docker-compose.yml +++ b/Api.Data.Triggers/.docker/docker-compose.yml @@ -1,6 +1,6 @@ services: - api.data.repository.triggers: - image: api.data.repository.triggers + api.data.triggers: + image: api.data.triggers hostname: api-data-triggers restart: on-failure ports: diff --git a/Api.Data.Triggers/.github/workflows/build-and-deploy.yml b/Api.Data.Triggers/.github/workflows/build-and-deploy.yml index 0cd03615..ea83ae68 100644 --- a/Api.Data.Triggers/.github/workflows/build-and-deploy.yml +++ b/Api.Data.Triggers/.github/workflows/build-and-deploy.yml @@ -15,7 +15,7 @@ env: DOTNET_SDK_VERSION: "10.0" DOTNET_ASPNET_VERSION: "10.0" DOTNET_EF_TOOLS_VERSION: "10.0" - AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_KUBERNETES_GROUP_KUBERNETES }} + AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} AZURE_GROUP_DELIVERY: ${{ vars.AZURE_RESOURCE_GROUP_DELIVERY }} AZURE_GROUP_DATABASE : ${{ vars.AZURE_RESOURCE_GROUP_DATABASE }} AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} diff --git a/Api.Documentation.Csp/.github/workflows/build-and-deploy.yml b/Api.Documentation.Csp/.github/workflows/build-and-deploy.yml index 10b03dd2..291c5305 100644 --- a/Api.Documentation.Csp/.github/workflows/build-and-deploy.yml +++ b/Api.Documentation.Csp/.github/workflows/build-and-deploy.yml @@ -60,6 +60,7 @@ jobs: $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; + echo "KUBERNETES_CLUSTER=$env:KUBERNETES_CLUSTER" >> $env:GITHUB_ENV; - name: Build Solution shell: pwsh diff --git a/Api.Eventing.RabbitMq/.github/workflows/build-and-deploy.yml b/Api.Eventing.RabbitMq/.github/workflows/build-and-deploy.yml index db8f69b2..1ba49fe9 100644 --- a/Api.Eventing.RabbitMq/.github/workflows/build-and-deploy.yml +++ b/Api.Eventing.RabbitMq/.github/workflows/build-and-deploy.yml @@ -10,7 +10,7 @@ on: env: APP_NAME: Api.Eventing.RabbitMq IMAGE_NAME: api.eventing.rabbitmq - SERVICE_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" diff --git a/Api.Eventing.RabbitMq/.kubernetes/deployment.yaml b/Api.Eventing.RabbitMq/.kubernetes/deployment.yaml index 5a30d851..98dc44e8 100644 --- a/Api.Eventing.RabbitMq/.kubernetes/deployment.yaml +++ b/Api.Eventing.RabbitMq/.kubernetes/deployment.yaml @@ -38,12 +38,12 @@ spec: - containerPort: 8080 imagePullPolicy: Always env: - - name: Eventing__Credentials__Host + - name: Eventing__Host valueFrom: secretKeyRef: name: rabbitmq-default-user key: host - - name: Eventing__Credentials__Port + - name: Eventing__Port valueFrom: secretKeyRef: name: rabbitmq-default-user diff --git a/Api.HealthChecks/.github/workflows/build-and-deploy.yml b/Api.HealthChecks/.github/workflows/build-and-deploy.yml index 67e919e4..a1f110e1 100644 --- a/Api.HealthChecks/.github/workflows/build-and-deploy.yml +++ b/Api.HealthChecks/.github/workflows/build-and-deploy.yml @@ -11,6 +11,7 @@ env: APP_NAME: Api.HealthChecks IMAGE_NAME: api.healthchecks SERVICE_NAME: api-healthchecks + SUB_DOMAIN_NAME: nano VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' DOTNET_SDK_VERSION: "10.0" DOTNET_ASPNET_VERSION: "10.0" diff --git a/Api.Hosting.MultipartLimits/.github/workflows/build-and-deploy.yml b/Api.Hosting.MultipartLimits/.github/workflows/build-and-deploy.yml index c6283627..994f8cd4 100644 --- a/Api.Hosting.MultipartLimits/.github/workflows/build-and-deploy.yml +++ b/Api.Hosting.MultipartLimits/.github/workflows/build-and-deploy.yml @@ -10,7 +10,7 @@ on: env: APP_NAME: Api.Hosting.MultipartLimits IMAGE_NAME: api.hosting.multipartlimits - SERVICE_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" diff --git a/Api.Metrics/.kubernetes/service-monitor.yaml b/Api.Metrics/.kubernetes/service-monitor.yaml index c64eb7fa..20ed7e41 100644 --- a/Api.Metrics/.kubernetes/service-monitor.yaml +++ b/Api.Metrics/.kubernetes/service-monitor.yaml @@ -6,7 +6,7 @@ metadata: spec: selector: matchLabels: - app.kubernetes.io/name: %SERVICE_NAME% + app: %SERVICE_NAME% endpoints: - port: http path: /metrics diff --git a/Api.Metrics/.kubernetes/service.yaml b/Api.Metrics/.kubernetes/service.yaml index 2d8e20b2..6cb76e1d 100644 --- a/Api.Metrics/.kubernetes/service.yaml +++ b/Api.Metrics/.kubernetes/service.yaml @@ -3,6 +3,8 @@ kind: Service metadata: name: %SERVICE_NAME% namespace: %KUBERNETES_NAMESPACE% + labels: + app: %SERVICE_NAME% spec: ports: - name: http diff --git a/Api.Metrics/README.md b/Api.Metrics/README.md index a1f97860..0ef4df42 100644 --- a/Api.Metrics/README.md +++ b/Api.Metrics/README.md @@ -51,7 +51,7 @@ metadata: spec: selector: matchLabels: - app.kubernetes.io/name: %SERVICE_NAME% + app: %SERVICE_NAME% endpoints: - port: http path: /metrics diff --git a/Api.Storage.Azure/.kubernetes/deployment.yaml b/Api.Storage.Azure/.kubernetes/deployment.yaml index f2e7ad15..cd37e02e 100644 --- a/Api.Storage.Azure/.kubernetes/deployment.yaml +++ b/Api.Storage.Azure/.kubernetes/deployment.yaml @@ -79,11 +79,6 @@ spec: 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: diff --git a/Api.Storage.Azure/.kubernetes/storage-pv.yaml b/Api.Storage.Azure/.kubernetes/storage-pv.yaml index 5bc60f12..2b483af0 100644 --- a/Api.Storage.Azure/.kubernetes/storage-pv.yaml +++ b/Api.Storage.Azure/.kubernetes/storage-pv.yaml @@ -4,7 +4,7 @@ metadata: name: %SERVICE_NAME%-azurefile-pv-%VOLUME_NAME_SUFFIX% spec: capacity: - storage: %STORAGE_SIZE% + storage: %STORAGE_SIZE%Gi accessModes: - ReadWriteMany persistentVolumeReclaimPolicy: Retain diff --git a/Api.Storage.Azure/.kubernetes/storage-pvc.yaml b/Api.Storage.Azure/.kubernetes/storage-pvc.yaml index 8ce63544..4124423d 100644 --- a/Api.Storage.Azure/.kubernetes/storage-pvc.yaml +++ b/Api.Storage.Azure/.kubernetes/storage-pvc.yaml @@ -9,5 +9,5 @@ spec: storageClassName: azurefile-static resources: requests: - storage: %STORAGE_SIZE% + storage: %STORAGE_SIZE%Gi volumeName: %SERVICE_NAME%-azurefile-pv-%VOLUME_NAME_SUFFIX% \ No newline at end of file diff --git a/Api.Storage.Local/.github/workflows/build-and-deploy.yml b/Api.Storage.Local/.github/workflows/build-and-deploy.yml index 13b0c3d9..b72d1a10 100644 --- a/Api.Storage.Local/.github/workflows/build-and-deploy.yml +++ b/Api.Storage.Local/.github/workflows/build-and-deploy.yml @@ -33,7 +33,7 @@ env: KUBERNETES_MEMORY_SCALING: 180 KUBERNETES_CPU_REQUEST: 200m KUBERNETES_CPU_LIMIT: 600m - STORAGE_SIZE: 1000 + STORAGE_SIZE: 1000Gi STORAGE_SHARE_NAME: nano-storage-local ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} concurrency: diff --git a/Api._Blank/.github/workflows/build-and-deploy.yml b/Api._Blank/.github/workflows/build-and-deploy.yml index 2ee847e3..1095c677 100644 --- a/Api._Blank/.github/workflows/build-and-deploy.yml +++ b/Api._Blank/.github/workflows/build-and-deploy.yml @@ -46,6 +46,7 @@ jobs: - ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} permissions: contents: write + packages: write steps: - uses: actions/checkout@v6 diff --git a/Console.Data.InMemory/.github/workflows/build-and-deploy.yml b/Console.Data.InMemory/.github/workflows/build-and-deploy.yml index 62befd94..df680f86 100644 --- a/Console.Data.InMemory/.github/workflows/build-and-deploy.yml +++ b/Console.Data.InMemory/.github/workflows/build-and-deploy.yml @@ -16,7 +16,6 @@ env: DOTNET_ASPNET_VERSION: "10.0" AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} AZURE_GROUP_DELIVERY: ${{ vars.AZURE_RESOURCE_GROUP_DELIVERY }} - AZURE_GROUP_DATABASE : ${{ vars.AZURE_RESOURCE_GROUP_DATABASE }} AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} diff --git a/Console.Data.MySql/.github/workflows/build-and-deploy.yml b/Console.Data.MySql/.github/workflows/build-and-deploy.yml index 7aef29c9..2541c524 100644 --- a/Console.Data.MySql/.github/workflows/build-and-deploy.yml +++ b/Console.Data.MySql/.github/workflows/build-and-deploy.yml @@ -197,8 +197,8 @@ jobs: - name: Kubernetes Deploy shell: pwsh run: | - 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; + 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"; diff --git a/Console.Data.SqLite/.kubernetes/cronjob.yaml b/Console.Data.SqLite/.kubernetes/cronjob.yaml index 1dd91868..ebf89707 100644 --- a/Console.Data.SqLite/.kubernetes/cronjob.yaml +++ b/Console.Data.SqLite/.kubernetes/cronjob.yaml @@ -53,5 +53,5 @@ spec: volumes: - name: %SERVICE_NAME%-volume persistentVolumeClaim: - claimName: %SERVICE_NAME%-pvc + claimName: %SERVICE_NAME%-data-pvc restartPolicy: OnFailure diff --git a/Console.Eventing.RabbitMq/.kubernetes/cronjob.yaml b/Console.Eventing.RabbitMq/.kubernetes/cronjob.yaml index f373d966..7e399c79 100644 --- a/Console.Eventing.RabbitMq/.kubernetes/cronjob.yaml +++ b/Console.Eventing.RabbitMq/.kubernetes/cronjob.yaml @@ -28,12 +28,12 @@ spec: image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% imagePullPolicy: Always env: - - name: Eventing__Credentials__Host + - name: Eventing__Host valueFrom: secretKeyRef: name: rabbitmq-default-user key: host - - name: Eventing__Credentials__Port + - name: Eventing__Port valueFrom: secretKeyRef: name: rabbitmq-default-user diff --git a/Console.Storage.Azure/.kubernetes/storage-pv.yaml b/Console.Storage.Azure/.kubernetes/storage-pv.yaml index 5bc60f12..2b483af0 100644 --- a/Console.Storage.Azure/.kubernetes/storage-pv.yaml +++ b/Console.Storage.Azure/.kubernetes/storage-pv.yaml @@ -4,7 +4,7 @@ metadata: name: %SERVICE_NAME%-azurefile-pv-%VOLUME_NAME_SUFFIX% spec: capacity: - storage: %STORAGE_SIZE% + storage: %STORAGE_SIZE%Gi accessModes: - ReadWriteMany persistentVolumeReclaimPolicy: Retain diff --git a/Console.Storage.Azure/.kubernetes/storage-pvc.yaml b/Console.Storage.Azure/.kubernetes/storage-pvc.yaml index 8ce63544..4124423d 100644 --- a/Console.Storage.Azure/.kubernetes/storage-pvc.yaml +++ b/Console.Storage.Azure/.kubernetes/storage-pvc.yaml @@ -9,5 +9,5 @@ spec: storageClassName: azurefile-static resources: requests: - storage: %STORAGE_SIZE% + storage: %STORAGE_SIZE%Gi volumeName: %SERVICE_NAME%-azurefile-pv-%VOLUME_NAME_SUFFIX% \ No newline at end of file diff --git a/Console.Storage.Local/.github/workflows/build-and-deploy.yml b/Console.Storage.Local/.github/workflows/build-and-deploy.yml index 6cb61dd1..621a285c 100644 --- a/Console.Storage.Local/.github/workflows/build-and-deploy.yml +++ b/Console.Storage.Local/.github/workflows/build-and-deploy.yml @@ -31,7 +31,7 @@ env: KUBERNETES_CPU_REQUEST: 50m KUBERNETES_CPU_LIMIT: 150m KUBERNETES_CRONJOB_SCHEDULE: "0 * * * *" - STORAGE_SIZE: 1000 + STORAGE_SIZE: 1000Gi STORAGE_SHARE_NAME: nano-storage-local DOTNET_ENVIRONMENT: ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} concurrency: diff --git a/Console.Storage.Local/.kubernetes/cronjob.yaml b/Console.Storage.Local/.kubernetes/cronjob.yaml index 514379a1..2d7c11ef 100644 --- a/Console.Storage.Local/.kubernetes/cronjob.yaml +++ b/Console.Storage.Local/.kubernetes/cronjob.yaml @@ -58,3 +58,4 @@ spec: claimName: %SERVICE_NAME%-pvc - name: tmp emptyDir: {} + restartPolicy: OnFailure From 9df3f3183d381d38a1237e5c2c6db5399b4e3589 Mon Sep 17 00:00:00 2001 From: vivet Date: Sun, 6 Sep 2026 13:29:57 +0200 Subject: [PATCH 4/6] Added AGENTS.md --- Api.ApiClients.Audit/AGENTS.md | 3114 +++++++++++++++++ Api.ApiClients.Entity/AGENTS.md | 3114 +++++++++++++++++ Api.ApiClients.RootLogIn/AGENTS.md | 3114 +++++++++++++++++ Api.ApiClients/AGENTS.md | 3114 +++++++++++++++++ Api.Auth.External.Custom/AGENTS.md | 3114 +++++++++++++++++ Api.Auth.RootLogin/AGENTS.md | 3114 +++++++++++++++++ Api.Authorization/AGENTS.md | 3114 +++++++++++++++++ Api.ContentNegotiation/AGENTS.md | 3114 +++++++++++++++++ Api.Cookies/AGENTS.md | 3114 +++++++++++++++++ Api.CustomConfigSection/AGENTS.md | 3114 +++++++++++++++++ Api.CustomMiddleware/AGENTS.md | 3114 +++++++++++++++++ Api.CustomService/AGENTS.md | 3114 +++++++++++++++++ Api.Data.Audit/AGENTS.md | 3114 +++++++++++++++++ Api.Data.EntityEvents/AGENTS.md | 3114 +++++++++++++++++ Api.Data.Identity.Auth.ApiKey/AGENTS.md | 3114 +++++++++++++++++ .../AGENTS.md | 3114 +++++++++++++++++ Api.Data.Identity.Auth.Jwt/AGENTS.md | 3114 +++++++++++++++++ Api.Data.Identity/AGENTS.md | 3114 +++++++++++++++++ Api.Data.InMemory/AGENTS.md | 3114 +++++++++++++++++ Api.Data.LazyLoading/AGENTS.md | 3114 +++++++++++++++++ Api.Data.MySql.Collation/AGENTS.md | 3114 +++++++++++++++++ Api.Data.MySql.Mappings/AGENTS.md | 3114 +++++++++++++++++ Api.Data.MySql.Spatial/AGENTS.md | 3114 +++++++++++++++++ Api.Data.MySql.StoredProcedures/AGENTS.md | 3114 +++++++++++++++++ Api.Data.MySql.Views/AGENTS.md | 3114 +++++++++++++++++ Api.Data.MySql/AGENTS.md | 3114 +++++++++++++++++ Api.Data.PostgreSQL.Spatial/AGENTS.md | 3114 +++++++++++++++++ Api.Data.PostgreSQL/AGENTS.md | 3114 +++++++++++++++++ Api.Data.Repository.AutoSave/AGENTS.md | 3114 +++++++++++++++++ Api.Data.Repository.Includes/AGENTS.md | 3114 +++++++++++++++++ Api.Data.SoftDelete/AGENTS.md | 3114 +++++++++++++++++ Api.Data.SqLite/AGENTS.md | 3114 +++++++++++++++++ Api.Data.SqlServer.Spatial/AGENTS.md | 3114 +++++++++++++++++ Api.Data.SqlServer/AGENTS.md | 3114 +++++++++++++++++ Api.Data.Triggers/AGENTS.md | 3114 +++++++++++++++++ Api.Documentation.Csp/AGENTS.md | 3114 +++++++++++++++++ Api.Documentation/AGENTS.md | 3114 +++++++++++++++++ Api.ErrorHandling/AGENTS.md | 3114 +++++++++++++++++ Api.Eventing.RabbitMq/AGENTS.md | 3114 +++++++++++++++++ Api.HealthChecks/AGENTS.md | 3114 +++++++++++++++++ Api.Hosting.Http/AGENTS.md | 3114 +++++++++++++++++ Api.Hosting.Https/AGENTS.md | 3114 +++++++++++++++++ Api.Hosting.MultipartLimits/AGENTS.md | 3114 +++++++++++++++++ Api.Localization/AGENTS.md | 3114 +++++++++++++++++ Api.Logging.Log4Net/AGENTS.md | 3114 +++++++++++++++++ Api.Logging.Microsoft/AGENTS.md | 3114 +++++++++++++++++ Api.Logging.NLog/AGENTS.md | 3114 +++++++++++++++++ Api.Logging.Serilog/AGENTS.md | 3114 +++++++++++++++++ Api.Metrics/AGENTS.md | 3114 +++++++++++++++++ Api.MultipartJson/AGENTS.md | 3114 +++++++++++++++++ .../AGENTS.md | 3114 +++++++++++++++++ .../AGENTS.md | 3114 +++++++++++++++++ Api.PolicyHeaders.Cors/AGENTS.md | 3114 +++++++++++++++++ Api.PolicyHeaders.ForwardedHeaders/AGENTS.md | 3114 +++++++++++++++++ Api.PolicyHeaders.FrameOptions/AGENTS.md | 3114 +++++++++++++++++ Api.PolicyHeaders.Hsts/AGENTS.md | 3114 +++++++++++++++++ Api.PolicyHeaders.ReferrerPolicy/AGENTS.md | 3114 +++++++++++++++++ Api.PolicyHeaders.Robots/AGENTS.md | 3114 +++++++++++++++++ Api.PolicyHeaders.XssProtection/AGENTS.md | 3114 +++++++++++++++++ Api.RequestTracing/AGENTS.md | 3114 +++++++++++++++++ Api.ResponseCache/AGENTS.md | 3114 +++++++++++++++++ Api.ResponseCompression/AGENTS.md | 3114 +++++++++++++++++ Api.Session/AGENTS.md | 3114 +++++++++++++++++ Api.StartupTasks/AGENTS.md | 3114 +++++++++++++++++ Api.StaticFiles/AGENTS.md | 3114 +++++++++++++++++ Api.Storage.Azure/AGENTS.md | 3114 +++++++++++++++++ Api.Storage.Local/AGENTS.md | 3114 +++++++++++++++++ Api.TimeZone/AGENTS.md | 3114 +++++++++++++++++ Api.Versioning/AGENTS.md | 3114 +++++++++++++++++ Api.VirusScan/AGENTS.md | 3114 +++++++++++++++++ Api._Blank/AGENTS.md | 3114 +++++++++++++++++ Console.CustomConfigSection/AGENTS.md | 3114 +++++++++++++++++ Console.CustomService/AGENTS.md | 3114 +++++++++++++++++ Console.Data.InMemory/AGENTS.md | 3114 +++++++++++++++++ Console.Data.MySql/AGENTS.md | 3114 +++++++++++++++++ Console.Data.PostgreSQL/AGENTS.md | 3114 +++++++++++++++++ Console.Data.SqLite/AGENTS.md | 3114 +++++++++++++++++ Console.Data.SqlServer/AGENTS.md | 3114 +++++++++++++++++ Console.Eventing.RabbitMq/AGENTS.md | 3114 +++++++++++++++++ Console.ExceptionHandling/AGENTS.md | 3114 +++++++++++++++++ Console.Localization/AGENTS.md | 3114 +++++++++++++++++ Console.Logging.Log4Net/AGENTS.md | 3114 +++++++++++++++++ Console.Logging.Microsoft/AGENTS.md | 3114 +++++++++++++++++ Console.Logging.NLog/AGENTS.md | 3114 +++++++++++++++++ Console.Logging.Serilog/AGENTS.md | 3114 +++++++++++++++++ Console.StartupTasks/AGENTS.md | 3114 +++++++++++++++++ Console.Storage.Azure/AGENTS.md | 3114 +++++++++++++++++ Console.Storage.Local/AGENTS.md | 3114 +++++++++++++++++ Console.Workers/AGENTS.md | 3114 +++++++++++++++++ Console._Blank/AGENTS.md | 3114 +++++++++++++++++ Web._Blank/AGENTS.md | 3114 +++++++++++++++++ 91 files changed, 283374 insertions(+) create mode 100644 Api.ApiClients.Audit/AGENTS.md create mode 100644 Api.ApiClients.Entity/AGENTS.md create mode 100644 Api.ApiClients.RootLogIn/AGENTS.md create mode 100644 Api.ApiClients/AGENTS.md create mode 100644 Api.Auth.External.Custom/AGENTS.md create mode 100644 Api.Auth.RootLogin/AGENTS.md create mode 100644 Api.Authorization/AGENTS.md create mode 100644 Api.ContentNegotiation/AGENTS.md create mode 100644 Api.Cookies/AGENTS.md create mode 100644 Api.CustomConfigSection/AGENTS.md create mode 100644 Api.CustomMiddleware/AGENTS.md create mode 100644 Api.CustomService/AGENTS.md create mode 100644 Api.Data.Audit/AGENTS.md create mode 100644 Api.Data.EntityEvents/AGENTS.md create mode 100644 Api.Data.Identity.Auth.ApiKey/AGENTS.md create mode 100644 Api.Data.Identity.Auth.External.Custom/AGENTS.md create mode 100644 Api.Data.Identity.Auth.Jwt/AGENTS.md create mode 100644 Api.Data.Identity/AGENTS.md create mode 100644 Api.Data.InMemory/AGENTS.md create mode 100644 Api.Data.LazyLoading/AGENTS.md create mode 100644 Api.Data.MySql.Collation/AGENTS.md create mode 100644 Api.Data.MySql.Mappings/AGENTS.md create mode 100644 Api.Data.MySql.Spatial/AGENTS.md create mode 100644 Api.Data.MySql.StoredProcedures/AGENTS.md create mode 100644 Api.Data.MySql.Views/AGENTS.md create mode 100644 Api.Data.MySql/AGENTS.md create mode 100644 Api.Data.PostgreSQL.Spatial/AGENTS.md create mode 100644 Api.Data.PostgreSQL/AGENTS.md create mode 100644 Api.Data.Repository.AutoSave/AGENTS.md create mode 100644 Api.Data.Repository.Includes/AGENTS.md create mode 100644 Api.Data.SoftDelete/AGENTS.md create mode 100644 Api.Data.SqLite/AGENTS.md create mode 100644 Api.Data.SqlServer.Spatial/AGENTS.md create mode 100644 Api.Data.SqlServer/AGENTS.md create mode 100644 Api.Data.Triggers/AGENTS.md create mode 100644 Api.Documentation.Csp/AGENTS.md create mode 100644 Api.Documentation/AGENTS.md create mode 100644 Api.ErrorHandling/AGENTS.md create mode 100644 Api.Eventing.RabbitMq/AGENTS.md create mode 100644 Api.HealthChecks/AGENTS.md create mode 100644 Api.Hosting.Http/AGENTS.md create mode 100644 Api.Hosting.Https/AGENTS.md create mode 100644 Api.Hosting.MultipartLimits/AGENTS.md create mode 100644 Api.Localization/AGENTS.md create mode 100644 Api.Logging.Log4Net/AGENTS.md create mode 100644 Api.Logging.Microsoft/AGENTS.md create mode 100644 Api.Logging.NLog/AGENTS.md create mode 100644 Api.Logging.Serilog/AGENTS.md create mode 100644 Api.Metrics/AGENTS.md create mode 100644 Api.MultipartJson/AGENTS.md create mode 100644 Api.PolicyHeaders.ContentSecurityPolicy/AGENTS.md create mode 100644 Api.PolicyHeaders.ContentTypeOptions/AGENTS.md create mode 100644 Api.PolicyHeaders.Cors/AGENTS.md create mode 100644 Api.PolicyHeaders.ForwardedHeaders/AGENTS.md create mode 100644 Api.PolicyHeaders.FrameOptions/AGENTS.md create mode 100644 Api.PolicyHeaders.Hsts/AGENTS.md create mode 100644 Api.PolicyHeaders.ReferrerPolicy/AGENTS.md create mode 100644 Api.PolicyHeaders.Robots/AGENTS.md create mode 100644 Api.PolicyHeaders.XssProtection/AGENTS.md create mode 100644 Api.RequestTracing/AGENTS.md create mode 100644 Api.ResponseCache/AGENTS.md create mode 100644 Api.ResponseCompression/AGENTS.md create mode 100644 Api.Session/AGENTS.md create mode 100644 Api.StartupTasks/AGENTS.md create mode 100644 Api.StaticFiles/AGENTS.md create mode 100644 Api.Storage.Azure/AGENTS.md create mode 100644 Api.Storage.Local/AGENTS.md create mode 100644 Api.TimeZone/AGENTS.md create mode 100644 Api.Versioning/AGENTS.md create mode 100644 Api.VirusScan/AGENTS.md create mode 100644 Api._Blank/AGENTS.md create mode 100644 Console.CustomConfigSection/AGENTS.md create mode 100644 Console.CustomService/AGENTS.md create mode 100644 Console.Data.InMemory/AGENTS.md create mode 100644 Console.Data.MySql/AGENTS.md create mode 100644 Console.Data.PostgreSQL/AGENTS.md create mode 100644 Console.Data.SqLite/AGENTS.md create mode 100644 Console.Data.SqlServer/AGENTS.md create mode 100644 Console.Eventing.RabbitMq/AGENTS.md create mode 100644 Console.ExceptionHandling/AGENTS.md create mode 100644 Console.Localization/AGENTS.md create mode 100644 Console.Logging.Log4Net/AGENTS.md create mode 100644 Console.Logging.Microsoft/AGENTS.md create mode 100644 Console.Logging.NLog/AGENTS.md create mode 100644 Console.Logging.Serilog/AGENTS.md create mode 100644 Console.StartupTasks/AGENTS.md create mode 100644 Console.Storage.Azure/AGENTS.md create mode 100644 Console.Storage.Local/AGENTS.md create mode 100644 Console.Workers/AGENTS.md create mode 100644 Console._Blank/AGENTS.md create mode 100644 Web._Blank/AGENTS.md diff --git a/Api.ApiClients.Audit/AGENTS.md b/Api.ApiClients.Audit/AGENTS.md new file mode 100644 index 00000000..ccb36313 --- /dev/null +++ b/Api.ApiClients.Audit/AGENTS.md @@ -0,0 +1,3114 @@ +# AGENTS.md β€” Nano Framework + +Implementation reference for building applications with Nano. Structured to mirror the module READMEs in this +repository (`Nano.App`, `Nano.App.Api`, `Nano.App.Console`, `Nano.App.Web`, `Nano.Logging`, `Nano.Data`, +`Nano.Eventing`, `Nano.Storage`), so a section here maps 1:1 to a section there. + +--- + +## Solution Structure + +Every Nano application β€” Api, Web, or Console β€” follows the same predictable solution layout. `{name}` is the +application's own name (e.g. `Svc.Accounts`); `{name}.Models` is a **separate, sibling project**, not nested +inside `{name}/`. + +| Directory / File | API | WEB | CON | Description | +| -------------------------------------------------------- | --- | --- | --- | ------------------------------------------------------------------------------------------------------------------------- | +| `{name}.sln` | βœ“ | βœ“ | βœ“ | The Visual Studio solution file, at the solution root. | +| `{name}/{name}.csproj` | βœ“ | βœ“ | βœ“ | The application project file. | +| `{name}/Program.cs` | βœ“ | βœ“ | βœ“ | Entry point β€” where the application is configured, built, and run. | +| `{name}/Properties/InternalsVisibleTo.cs` | βœ“ | βœ“ | βœ“ | Exposes internal types to the test project. | +| `{name}/appsettings.json` | βœ“ | βœ“ | βœ“ | Default application configuration. | +| `{name}/appsettings.{environment}.json` | βœ“ | βœ“ | βœ“ | Overrides for `Development`, `Staging`, `Production`. | +| `{name}/Controllers/` | βœ“ | βœ“ | βœ— | Concrete controllers (conventional location, not a hard requirement). | +| `{name}/Data/` | βœ“ | βœ“ | βœ“ | `DbContext`, `DbContextFactory`, and `Mappings/` (conventional location). | +| `{name}/Migrations/` | βœ“ | βœ“ | βœ“ | EF Core migrations (conventional location, when a SQL data provider is used). | +| `{name}/wwwroot/` | βœ“ | βœ“ | βœ— | Static/dynamic web content root. | +| `{name}/Dockerfile.Local` | βœ“ | βœ“ | βœ“ | Used by Docker Compose in `Development`; must stay in the application project folder. | +| `{name}.Models/{name}.Models.csproj` | βœ“ | βœ“ | βœ— | Sibling project holding entity models, query criteria, and API client (Requests/Api). Publishable as its own NuGet for sharing models + API client with consumers. Should reference at minimum `Nano.App`. | +| `{name}.Models/Data/` | βœ“ | βœ“ | βœ— | Entity models (conventional location). | +| `{name}.Models/Criterias/` | βœ“ | βœ“ | βœ— | Query criteria classes (conventional location). | +| `{name}.Models/Api/` | βœ“ | βœ“ | βœ— | API client + `Requests/` (conventional location, for apps exposing a typed client to consumers). | +| `.tests/Tests.{name}/Tests.{name}.csproj` | βœ“ | βœ“ | βœ“ | Test project β€” empty by default, demonstrates where unit/integration tests belong. | +| `.tests/Tests.{name}/Properties/DoNotParallelize.cs` | βœ“ | βœ“ | βœ“ | Ensures tests are not parallelized. | +| `.docker/docker-compose.dcproj` | βœ“ | βœ“ | βœ“ | Docker Compose project used by Visual Studio for local orchestration. | +| `.docker/docker-compose.yml` | βœ“ | βœ“ | βœ“ | Docker Compose spec for local (`Development`) orchestration. | +| `.kubernetes/configmap.yaml` | βœ“ | βœ“ | βœ“ | Kubernetes ConfigMap. | +| `.kubernetes/autoscaler.yaml` | βœ“ | βœ“ | βœ— | Kubernetes Horizontal Pod Autoscaler. | +| `.kubernetes/deployment.yaml` | βœ“ | βœ“ | βœ— | Kubernetes Deployment. | +| `.kubernetes/service.yaml` | βœ“ | βœ“ | βœ— | Kubernetes Service. | +| `.kubernetes/httproute.yaml` | (βœ“) | (βœ“) | βœ— | Kubernetes HTTPRoute _(optional, public-facing apps only)_. | +| `.kubernetes/cronjob.yaml` | βœ— | βœ— | βœ“ | Kubernetes CronJob (Console apps run as scheduled jobs, not long-running Deployments). | +| `.github/config/slack.yml` | βœ“ | βœ“ | βœ“ | Build/deploy Slack notifications _(optional)_. | +| `.github/workflows/build-and-deploy.yml` | βœ“ | βœ“ | βœ“ | CI/CD workflow β€” build, test, publish, deploy. | +| `Dockerfile` | βœ“ | βœ“ | βœ“ | Container image build for `Staging`/`Production`, at the solution root. | +| `.dockerignore` / `.gitignore` | βœ“ | βœ“ | βœ“ | Solution root. | +| `README.md` / `icon.png` / `LICENSE` | (βœ“) | (βœ“) | (βœ“) | Solution root, optional β€” used for the repo and any published NuGet packages. | + +Folder names like `Controllers/`, `Data/`, `Criterias/`, `Api/`, and `Migrations/` are convention, not a +framework requirement β€” Nano discovers controllers, mappings, and data providers by type, not by folder +location. As each feature section below is filled in, it will also note where new files of that kind +conventionally belong. + +**NuGet packages**: for a quick start, add `NanoCore` (all-inclusive) to `{name}.Models` only β€” since `{name}` +references `{name}.Models` via `ProjectReference`, every Nano package flows into the app project transitively, so +no Nano package reference is needed there directly. This is what Nano.Templates itself does. Once you know which +providers you're actually using, switch to referencing only the specific packages you need (e.g. +`Nano.Data.PostgreSQL` instead of the whole graph) β€” smaller dependency footprint, and it makes provider choices +explicit in the `.csproj` rather than implicit via a meta-package. + +**Non-`Guid` identity**: Nano defaults every generic surface to `Guid` via a non-generic shorthand +(`BaseEntity` = `BaseEntity`, `IRepository` = `Repository`, etc.). ⭐ It's highly +recommended to just use `Guid` throughout β€” it's the path every non-generic shorthand and every real example in +this doc is built around. Using a different identity type (`int`, `long`, `string`, or a custom +`IEquatable`) means threading the same `TIdentity` through **every** one of these consistently β€” there's no +single place that "sets" it once: + +- **Data**: `AddNanoData()`, `BaseDbContext`, every entity base class + (`BaseEntity`, etc.) and mapping base class. +- **Repository**: the concrete `Repository` registered behind `IRepository`. +- **Controllers**: `BaseEntityController` and siblings, `BaseAuthController`, + `BaseAuditController`. +- **Authentication**: `IAuthRepository`, `IAuthIdentityRepository`, `IIdentityRepository`. +- **Api Client**: `BaseApiClient`, `BaseIdentityApiClient`, and generic requests + (`DetailsRequest`, `DeleteRequest`, etc.). +- **Audit**: `AuditEntry`, `AuditEntryProperty`. +- **Identity entity models**: `IdentityUserEx`, `IdentityRole`, etc. + +Mixing identity types across these β€” e.g. an `int`-keyed entity registered against a `Guid`-typed repository β€” +doesn't compile or bind correctly. If everything stays `Guid`, none of this matters; it's only relevant the +moment one non-default identity type is chosen anywhere in the app. + +--- + +## Nano.App + +Common services shared by every Nano application type (Api, Console, Web). Transitive β€” never referenced +directly by an app project. + +### Environment + +Nano is environment-neutral: behavior differs only through `appsettings.{environment}.json`, never through +environment-specific code. The environment is read from `DOTNET_ENVIRONMENT` or `ASPNETCORE_ENVIRONMENT`, +defaulting to `Development`. + +| Environment | Type | Description | +| ------------- | ------ | ----------------------------- | +| `Development` | Local | Local development machine. | +| `Staging` | Cloud | Cloud Kubernetes deployment. | +| `Production` | Cloud | Cloud Kubernetes deployment. | + +### Configuration + +Standard .NET configuration providers, with precedence (later overrides earlier): + +1. `appsettings.json` +2. `appsettings.{environment}.json` +3. Command-line arguments +4. Environment variables +5. User secrets (`Development` only) + +Two deviations from stock .NET behavior: +- An **empty** configuration section is mapped with all default values β€” it is not treated as absent. +- Setting a section to **`null`** in an environment-specific file removes/overrides a section defined in the + base `appsettings.json` (stock .NET silently ignores a `null` override; Nano honors it as a deletion). + +### Null Logger + +If no logging provider is registered (see [Nano.Logging](#nanologging)), Nano still registers `ILoggerFactory`, +`ILogger`, and `ILogger` β€” backed by a `NullLogger` that discards everything. This is a safety fallback so +code that injects `ILogger` never fails to resolve, even with no logging provider configured. + +### Api Clients + +This is the mechanism for one Nano application to call another over HTTP with a typed, strongly-modeled client β€” +full CRUD against the target's entities, authentication, and identity management, without hand-building HTTP +requests. It's how internal services expose their models/entities to other applications (typically via a NuGet +built from their `{name}.Models` project β€” see [Solution Structure](#solution-structure)), and how a +publicly-exposed gateway API composes several internal services into one faΓ§ade. + +**Where the code lives**: the client class and its custom request types live in the *owning* service's +`{name}.Models/Api/` project (e.g. `MyService.Models/Api/MyApi.cs`, with custom requests under +`Api/Requests/`). A consuming application references that project (or its published NuGet) and injects the +client class directly β€” no manual DI registration needed. + +#### Defining a client + +Derive from `BaseApiClient` (`Guid` identity), `BaseApiClient` (custom identity type), or β€” if the +target application has Identity configured β€” `BaseIdentityApiClient`/`BaseIdentityApiClient`, where `TUser` is the target's `IEntityUser` model. The constructor must take exactly `ApiClient`. + +Three shapes: + +```csharp +// Bare pass-through β€” no custom methods, relies entirely on the built-in .Entity/.Auth/.Audit groups +public class MyApi(ApiClient apiClient) : BaseApiClient(apiClient); +``` + +```csharp +// Custom methods only, wrapping one hand-defined request each +public class MyOtherApi(ApiClient apiClient) : BaseApiClient(apiClient) +{ + // No response β€” InvokeAsync + public virtual Task MyMethodAsync(MyModel model, CancellationToken cancellationToken = default) + => this.InvokeAsync(new MyRequest { Model = model }, cancellationToken); + + // Typed response β€” InvokeAsync; MyResponse is a plain POCO, no base type required + public virtual Task GetMyResponseAsync(MyRequest request, CancellationToken cancellationToken = default) + => this.InvokeAsync(request, cancellationToken); +} +``` + +```csharp +// Identity-backed target β€” adds the .Identity method group +public class MyApi(ApiClient apiClient) : BaseIdentityApiClient(apiClient) +{ + public virtual Task GetByEmailAsync(string emailAddress, CancellationToken cancellationToken = default) + => this.InvokeAsync(new GetByEmailRequest { EmailAddress = emailAddress }, cancellationToken); +} +``` + +#### Built-in method groups + +Available as properties on the client instance β€” no implementation needed, just call them: + +| Group | Available on | Covers | +| -------------- | ---------------------------------------- | ------------------------------------------------------------------------------------ | +| `.Entity` | `BaseApiClient` | Full CRUD against any entity of the target app: `GetAsync`, `GetManyAsync`, `QueryAsync`, `QueryFirstAsync`, `QueryCountAsync`, `CreateAsync`/`CreateOrEditAsync`/`CreateOrGetAsync`/`CreateAndGetAsync`/`CreateManyAsync`(`Bulk`), `EditAsync`/`EditAndGetAsync`/`EditManyAsync`(`Bulk`)/`EditQueryAsync`(`Bulk`), `DeleteAsync`/`DeleteManyAsync`(`Bulk`)/`DeleteQueryAsync`(`Bulk`). Mirrors the entity controller route table 1:1 β€” see [Controllers Β§ Full CRUD route table](#full-crud-route-table). | +| `.Auth` | `BaseApiClient` | `LogInAsync`, `LogInRootAsync`, `LogInApiKeyAsync`, `LogInExternalAsync`, `LogInRefreshAsync`, `LogOutAsync`, `GetExternalSchemesAsync`. | +| `.Audit` | `BaseApiClient` | Read-only access to the target's `AuditEntry` log: `GetAsync`/`GetManyAsync`/`QueryAsync`/`QueryFirstAsync`/`QueryCountAsync`. | +| `.Identity` | `BaseIdentityApiClient` | Sign-up, password set/change/reset (+ token generation), email/phone change/confirm (+ token generation), roles, claims, external logins, refresh tokens, API keys. | + +An endpoint not enabled on the target application (e.g. `.Auth` when the target has no authentication configured) +returns `404` β€” surfaced as `null`, not an exception (see Gotchas below). + +Real usage β€” a controller composing multiple clients (an identity-backed `MyApi` plus a custom-methods-only +`MyOtherApi`) into one gateway endpoint: + +```csharp +public class MyUserController(ILogger logger, MyApi myApi, MyOtherApi myOtherApi) + : BaseController(logger) +{ + public virtual async Task GetMyUserAsync(Guid id, CancellationToken cancellationToken = default) + { + var entity = await myApi.Entity.GetAsync(id, cancellationToken); + return entity == null ? this.NotFound() : this.Ok(entity); + } + + public virtual async Task SignUpAsync([FromBody][Required] MyUser entity, CancellationToken cancellationToken = default) + { + var user = await myApi.Identity.SignUpAsync(new SignUpRequest { SignUp = new SignUp { User = entity } }, cancellationToken); + + await myOtherApi.MyMethodAsync(new MyModel { UserId = user.Id }, cancellationToken); + + return this.Created("signup", user); + } +} +``` + +#### Custom requests (endpoints beyond CRUD/Auth/Identity) + +1. Derive a request from `BaseRequest`, annotated with an action attribute naming the HTTP verb + relative route: + `[GetAction]`, `[PostAction]`, `[PutAction]`, `[DeleteAction]`, `[PatchAction]`, `[QueryAction]`, `[HeadAction]`, + `[OptionsAction]`, `[ConnectAction]`. +2. Annotate properties with parameter attributes: + +| Attribute | Purpose | +| ------------- | ------------------------------------------------------------------------------------------------------------------ | +| `[Route(Order = n)]` | Positional route-template substitution (`{n}` placeholders in the action's route string, filled in `Order` sequence). | +| `[Query]` | Querystring parameter (scalar types); optional `Name` override. | +| `[Body]` | The JSON request body (one complex object). | +| `[Form]` | A `multipart/form-data` field β€” scalar, or `IFormFile`/`FileInfo`/`FileStream`/`Stream`/`NamedStream`; complex objects need `[FromFormBody]` server-side. Mutually exclusive with `[Body]`. | +| `[Header(Name=..., ValuePrefix=...)]` | An HTTP header key/value. | + +Four shapes, covering every parameter attribute: + +```csharp +[GetAction("all")] +public class GetAllRequest : BaseRequest; // no params β€” controller inferred from TResponse (e.g. IEnumerable -> "MyEntities") + +[GetAction("by-name")] +public class MyQueryRequest : BaseRequest +{ + [Query] public virtual string Name { get; set; } = null!; +} + +[GetAction("{id}/file/{type}")] +public class MyFileRequest : BaseRequest +{ + [Route(Order = 0)] public virtual Guid Id { get; set; } + [Route(Order = 1)] public virtual MyEnum Type { get; set; } + + public MyFileRequest() { this.Controller = "MyEntities"; } // explicit override β€” route doesn't match a pluralized TResponse +} + +[PostAction("{id}/file/set")] +public class SetMyFileRequest : BaseRequest +{ + [Route] public virtual Guid Id { get; set; } + [Form] public virtual IFormFile File { get; set; } = null!; + + public SetMyFileRequest() { this.Controller = "MyEntities"; } +} +``` + +3. Add a method to the client, calling `InvokeAsync` (no response) or `InvokeAsync` + (typed response β€” use `NamedStream` or `Stream` for file downloads). + +**Controller resolution**: if a request doesn't set `this.Controller` explicitly in its constructor, it's +inferred as the pluralized `TResponse` type name (e.g. `IEnumerable` β†’ `MyEntities`). Set it explicitly +whenever the route doesn't naturally match the response type, or the request has no typed response at all. + +**Keep the route string in sync with the server.** Both sides declare the same route segment independently β€” the +action attribute here, and `[Route(...)]` on the target controller's action β€” with nothing enforcing they match. +Nano's own built-in requests avoid this by referencing shared constants (`Nano.Common.Consts.ActionRoutes`) from +both sides, e.g. `BaseEntityViewController` uses `[Route(ActionRoutes.INDEX)]` while the built-in `IndexRequest` +uses `[PostAction(ActionRoutes.INDEX)]` β€” one string, referenced twice, so a rename can't silently break the +client without also breaking the build. For your own custom endpoints, define the route segment as a constant in +a `Consts` class inside the shared `{name}.Models` project (visible to both the owning API project and any +client-Api consumer) and reference it from both the request's action attribute and the controller's `[Route(...)]`, +instead of retyping the same literal string in two places. + +#### Configuration + +Registered automatically β€” no `services.AddNanoApiClient()` call needed. Every `BaseApiClient` subclass in +the entry assembly whose class name matches a key under `App:Apis` gets wired up (`HttpClient` + `ApiClient` + +the client instance) and becomes injectable. + +| Setting | Type | Default | Description | +| ---------------------------- | -------- | --------- | ------------------------------------------------------------------------------------ | +| `Host` | string | localhost | Target API host. | +| `Root` | string | api | Root path segment. | +| `Port` | int | 80 | Target port. | +| `UseSsl` | bool | false | Use HTTPS. | +| `Timeout` | TimeSpan | 00:00:30 | Request timeout. | +| `LogInRoot.Username` | string | null | Optional β€” auto-login as root if no inbound JWT is available to forward. | +| `LogInRoot.Password` | string | null | Optional β€” paired with `LogInRoot.Username`. | +| `HealthCheck.UnhealthyStatus` | enum | Unhealthy | Status reported when the target is unreachable. API/Web apps only. | + +```json +"App": { + "Apis": { + "MyApi": { + "Host": "my-service", + "Root": "api", + "Port": 8080, + "UseSsl": false, + "Timeout": "00:00:30", + "HealthCheck": { "UnhealthyStatus": "Unhealthy" } + } + } +} +``` + +⚠ The dictionary key **must exactly match the client's class name** (`MyApi` above) β€” this is the only +link between config and DI; there's no other place to declare which config entry a client uses. + +#### Authentication forwarding + +Outbound JWT is resolved in this order: `request.JwtTokenOverride` (explicit per-request override) β†’ the +current inbound request's own JWT (so a call made from inside a controller/worker action transparently forwards +the caller's identity β€” this is how a gateway application's controllers stay authenticated end-to-end into an +internal service) β†’ if `LogInRoot` is configured, an automatic root login (cached for the process lifetime). A +set of headers (`X-Api-Key`, `X-Forwarded-*`, request id, `Accept-Language`, timezone) is also forwarded +automatically from the inbound `HttpContext`, so locale/tenant/tracing context survives across service calls. + +Console workers (which have no inbound `HttpContext`) typically call only anonymous/unauthenticated endpoints to +avoid needing `LogInRoot` credentials β€” a worker with no `LogInRoot` configured at all can still call target +endpoints that are `[AllowAnonymous]`. + +#### Gotchas + +- A configured-but-never-injected client is **not** registered β€” Nano only wires up clients actually referenced + somewhere in the app. +- `404` responses return `null`, never throw β€” always null-check rather than try/catch. +- Other non-success responses throw `ProblemDetailsException` (structured `ProblemDetails`) or, if the body + isn't parseable as `ProblemDetails`, `ApiClientException` (raw body + status code). +- Every generic `.Entity` read method accepts an `includeDepth` parameter β€” thread your own controller's + `[FromQuery] int? includeDepth` through to it for end-to-end include-depth control, see [Include + Annotation](#include-annotation). + +### Start-Up Tasks + +One-time initialization work that must complete before the application starts accepting traffic (or, for +Console apps, before workers start) β€” cache warm-up, external dependency checks, or similar. Not the same +mechanism as the built-in database migration task Nano runs for a configured data provider. + +#### Defining a task + +Implement `IStartupTask` (`OnStartAsync`/`OnStopAsync`), or derive from `BaseStartupTask` to only need +`OnStartAsync` β€” its `OnStopAsync` defaults to `Task.CompletedTask`. + +```csharp +public class MyStartupTask(ILogger logger) : BaseStartupTask(logger) +{ + public override async Task OnStartAsync(CancellationToken cancellationToken = default) + { + // one-time init β€” cache warm-up, external dependency check, etc. + } + + // optional β€” only override if you need it; see the timing note below before relying on it + public override async Task OnStopAsync(CancellationToken cancellationToken = default) + { + // cleanup for what OnStartAsync acquired β€” runs right after OnStartAsync completes, not at real shutdown + } +} +``` + +No registration needed β€” just define the class in the entry assembly. Every non-abstract `IStartupTask` +implementation is discovered by reflection and registered `Scoped`; any other registered service, including +scoped ones, can be injected into the constructor. + +#### Execution + +All registered tasks' `OnStartAsync` run **concurrently** (`Task.WhenAll`), in one shared service scope, before +the application accepts requests. If any task throws, the exception propagates and **the application fails to +start** β€” a startup task is not allowed to fail silently. + +⚠ **`OnStopAsync` is not "runs at application shutdown."** Immediately after all `OnStartAsync` calls complete, +Nano's internal hosted service calls its own stop routine right away β€” which invokes every task's `OnStopAsync` +and decrements a shared readiness counter (`StartupTaskContext`). So `OnStopAsync` actually fires right after +`OnStartAsync` finishes, as a completion/cleanup hook β€” not tied to real application shutdown (though the host's +real shutdown sequence may also invoke it again). Use it for cleanup that belongs immediately after the task's +own startup work, not for logic that must run when the application actually stops. + +#### Readiness integration + +The same readiness counter backs the built-in *self* startup health check: once [Health Checks](#health-checks) +are enabled, the application isn't reported healthy/ready until every startup task's `OnStartAsync` **and** +`OnStopAsync` have completed. In Console apps, workers don't start until this same counter reaches zero β€” startup +tasks always run to completion before the first worker starts. + +Conventionally placed in a `Startup/` folder in the application project (not a hard requirement β€” discovered by +type, not location). + +### Custom Services + +Standard ASP.NET Core dependency injection β€” nothing Nano-specific beyond the extension point. Register anything +in the `ConfigureServices(...)` step alongside the `AddNanoX<...>()` provider calls: + +```csharp +NanoApiApplication + .ConfigureApp() + .ConfigureServices(services => + { + services.AddSingleton(); + }) + .Build() + .Run(); +``` + +### Custom Middleware + +Add middleware to the `IApplicationBuilder` delegate passed to `Build(...)`: + +```csharp +NanoApiApplication + .ConfigureApp() + .ConfigureServices(services => { /* ... */ }) + .Build(builder => + { + builder.Use((context, next) => + { + context.Response.Headers["MyHeader"] = "MyValue"; + + return next(); + }); + }) + .Run(); +``` + +⚠ Custom middleware is **appended to the end** of Nano's own middleware pipeline β€” it can't run earlier in the +pipeline than Nano's built-in middleware. + +⚠ Only API and Web applications support this β€” Console applications ignore the `Build(builder => ...)` delegate +entirely, since there's no HTTP pipeline to add middleware to. + +### Custom Configuration Section + +Define an options model, add a matching section to `appsettings.json`, and bind it with +`AddNanoConfigSection(name, out options)`: + +```csharp +public class MySectionModel +{ + // Properties... +} +``` + +```json +{ + "MySection": { } +} +``` + +```csharp +.ConfigureServices(services => +{ + services.AddNanoConfigSection("MySection", out var options); +}) +``` + +`options` is the bound instance, available immediately for further service registration in the same +`ConfigureServices` call; the section is also registered for standard `IOptions`/`IOptionsMonitor` +injection anywhere else. Binding uses the same validation as every other Nano section β€” +`ValidateDataAnnotationsRecursively().ValidateOnStart()` β€” so a `[Required]` property left unset fails at host +startup, not on first use. + +⚠ The section name must actually **exist** in configuration, even if empty (`"MySection": { }`) β€” an entirely +missing section throws `InvalidOperationException` at startup, it doesn't silently bind an all-defaults instance. + +Section names must not collide with Nano's own built-in sections: `App`, `Logging`, `Data`, `Eventing`, `Storage`. + +--- + +## Nano.App.Api + +`NanoApiApplication` β€” the ready-to-use API host template. + +### Registration + +```powershell +dotnet add package Nano.App.Api; +``` + +```csharp +NanoApiApplication + .ConfigureApp() + .ConfigureServices(x => + { + // Your services... + }) + .Build() + .Run(); +``` + +### Configuration + +The `App` section defines application-level behavior. + +| Setting | Type | Default | Description | +| ---------------------- | ---------- | ------- | --------------------------------------------------------------- | +| `Version` | string | 1.0.0.0 | Application version identifier. | +| `ShutdownTimeout` | int | 10 | Seconds to wait after SIGTERM before shutting down. | +| `Hosting` | object | default | See [Hosting](#hosting). | +| `HttpPolicyHeaders` | object | default | See [Http Policy Headers](#http-policy-headers). | +| `ResponseCache` | object | null | See [Response Cache](#response-cache). | +| `ResponseCompression` | object | null | See [Response Compression](#response-compression). | +| `Session` | object | null | See [Session](#session). | +| `TimeZone` | object | null | See [TimeZone](#timezone). | +| `Localization` | object | null | See [Localization](#localization). | +| `Documentation` | object | null | Swagger config. See [Documentation](#documentation). | +| `HealthCheck` | object | null | See [Health Checks](#health-checks). | +| `Metrics` | object | null | See [Metrics (OpenTelemetry)](#metrics-opentelemetry). | +| `VirusScan` | object | null | See [Virus Scan](#virus-scan). | +| `ErrorHandling` | object | default | See [Error Handling](#error-handling). | +| `Authentication` | object | default | See [Authentication](#authentication). | +| `Apis` | dictionary | [] | Named Nano API client configurations. See [Nano.App Β§ Api Clients](#api-clients). | + +```json +"App": { + "Version": "1.0.0.0", + "ShutdownTimeout": 10, + "Hosting": { }, + "HttpPolicyHeaders": { }, + "ResponseCache": null, + "ResponseCompression": null, + "Session": null, + "TimeZone": null, + "Localization": null, + "Documentation": null, + "HealthCheck": null, + "VirusScan": null, + "ErrorHandling": { }, + "Authentication": { }, + "Apis": [] +} +``` + +#### Hosting + +How the API is hosted on Kestrel. + +| Setting | Type | Default | Description | +| -------------------- | ------ | ------- | -------------------------------------------------- | +| `Root` | string | api | Root route prefix for application endpoints. | +| `Http` | object | default | See [Http](#http). | +| `Https` | object | null | See [Https](#https). | +| `MultipartLimits` | object | null | See [MultiPart Limits](#multipart-limits). | + +```json +"App": { + "Hosting": { + "Root": "api", + "Http": { }, + "Https": null, + "MultipartLimits": null + } +} +``` + +##### Http + +| Setting | Type | Default | Description | +| ------------------------ | ------ | ------- | ------------------------------------------- | +| `Ports` | array | [] | List of ports for HTTP. | +| `UseHttpsRedirection` | bool | false | Enforce HTTPS redirect for all requests. | + +```json +"App": { + "Hosting": { + "Http": { + "Ports": [], + "UseHttpsRedirection": false + } + } +} +``` + +⚠ At least one HTTP or HTTPS port must be specified. Avoid the default port 80 β€” it may trigger security +warnings in Kubernetes. + +##### Https + +Requires at least one port plus a certificate path and password. Intended primarily for local development β€” +`Staging`/`Production` TLS is handled at the gateway/cert-manager level, not via this config. + +| Setting | Type | Default | Description | +| ------------------------- | ------ | ------- | -------------------------------------------- | +| `Ports` | array | [] | List of ports for HTTPS. | +| `UseHttpsRequired` | bool | false | Enforce HTTPS for all requests. | +| `Certificate.Path` | string | null | Required. File path to the certificate. | +| `Certificate.Password` | string | null | Required. Password for the certificate. | + +```json +"App": { + "Hosting": { + "Http": { "UseHttpsRedirection": true }, + "Https": { + "Ports": [4443], + "Certificate": { + "Path": "/root/.dotnet/https/localhost.pfx", + "Password": "password" + }, + "UseHttpsRequired": true + } + } +} +``` + +⚠ Avoid the default HTTPS port 443 β€” it may trigger security warnings in Kubernetes. Configure this only in +`appsettings.Development.json`. + +##### Routing + +No configuration β€” routing is fully automatic. Routes are derived from the base controller a concrete controller +derives from; API versioning is integrated into the route automatically. All routes are normalized to lowercase. + +##### MultiPart Limits + +| Setting | Type | Default | Description | +| ---------------------- | ----- | -------- | ------------------------------------------------- | +| `MaxUploadBytes` | int | 33554432 | Maximum upload size in bytes (default 32 MB). | +| `KeepAliveTimeout` | int | 00:02:10 | Timeout for slow uploads. | + +```json +"App": { + "Hosting": { + "MultipartLimits": { + "MaxUploadBytes": 33554432, + "KeepAliveTimeout": 130 + } + } +} +``` + +⚠ Leaving this `null` allows unlimited uploads β€” fine if limits are enforced at the orchestration level, +otherwise set explicit limits. + +#### Http Policy Headers + +Parent config object for headers such as HSTS, XSS protection, CSP, CORS, and other browser-security policies. + +| Setting | Type | Default | Description | +| ---------------------- | ------ | ------- | ---------------------------------------------------- | +| `ContentType` | object | null | See [Content Type Options](#content-type-options). | +| `ReferrerPolicy` | object | null | See [Referrer Policy](#referrer-policy). | +| `FrameOptions` | object | null | See [Frame Options](#frame-options). | +| `XssProtection` | object | null | See [Xss Protection](#xss-protection). | +| `Csp` | object | null | See [Content Security Policy (CSP)](#content-security-policy-csp). | +| `Cors` | object | null | See [Cors](#cors). | +| `Hsts` | object | null | See [Strict Transport Security (Hsts)](#strict-transport-security-hsts). | +| `Robots` | object | null | See [Robots](#robots). | +| `ForwardedHeaders` | object | null | See [Forwarded Headers](#forwarded-headers). | + +```json +"App": { + "HttpPolicyHeaders": { + "ContentType": null, + "ReferrerPolicy": null, + "FrameOptions": null, + "XssProtection": null, + "Csp": null, + "Cors": null, + "Hsts": null, + "Robots": null, + "ForwardedHeaders": null + } +} +``` + +##### Content Type Options + +Sets the `X-Content-Type-Options` response header to prevent MIME type sniffing. + +| Setting | Type | Default | Description | +| ------------- | ---- | ------- | ---------------------------------------- | +| `NoSniff` | bool | true | If true, prevents MIME type sniffing. ⭐ recommended: `true`. | + +```json +"App": { + "HttpPolicyHeaders": { + "ContentType": { "NoSniff": true } + } +} +``` + +##### Referrer Policy + +Sets the `Referrer-Policy` response header, controlling how much referrer information is sent with requests. + +| Setting | Type | Default | Description | +| --------------------------- | ---- | -------- | --------------------------------- | +| `ReferrerPolicyHeader` | enum | Disabled | See policy values below. | + +```json +"App": { + "HttpPolicyHeaders": { + "ReferrerPolicy": { "ReferrerPolicyHeader": "Disabled" } + } +} +``` + +| Policy | Description | +| ---------------------------------- | ------------ | +| `Disabled` | Header not set. | +| `NoReferrer` | No referrer information sent. | +| `NoReferrerWhenDowngrade` | Full referrer unless HTTPS β†’ HTTP. | +| `SameOrigin` ⭐ | Full referrer for same-origin, none for cross-origin. | +| `Origin` | Only origin (no path/query) sent, always. | +| `StrictOrigin` | Origin only, and never HTTPS β†’ HTTP. | +| `OriginWhenCrossOrigin` | Full for same-origin, origin-only for cross-origin. | +| `StrictOriginWhenCrossOrigin` | Full for same-origin, origin-only cross-origin, none HTTPS β†’ HTTP. | +| `UnsafeUrl` | Full referrer always, including HTTPS β†’ HTTP. Unsafe. | + +The `[ReferrerPolicy]` action/controller attribute overrides the global setting per endpoint. + +##### Frame Options + +Sets `X-Frame-Options`, guarding against clickjacking by controlling `