diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
new file mode 100644
index 00000000..cfa292cd
--- /dev/null
+++ b/.github/FUNDING.yml
@@ -0,0 +1,3 @@
+# These are supported funding model platforms
+
+github:, [Nano-Core]
diff --git a/.github/workflows/build-and-deploy.yml b/.github/workflows/build-and-deploy.yml
new file mode 100644
index 00000000..130b0217
--- /dev/null
+++ b/.github/workflows/build-and-deploy.yml
@@ -0,0 +1,34 @@
+name: Build and Deploy
+on:
+ pull_request:
+ branches:
+ - master
+ push:
+ branches:
+ - master
+env:
+ VERSION: 10.0.0-rc1
+jobs:
+ build-and-deploy:
+ runs-on: ubuntu-latest
+ permissions:
+ contents: write
+ id-token: write
+ concurrency:
+ group: ${{ github.repository }}
+ cancel-in-progress: false
+ steps:
+ - uses: actions/checkout@v6
+
+ - name: GitHub Release
+ if: github.ref == 'refs/heads/master'
+ uses: ncipollo/release-action@v1
+ with:
+ tag: v${{ env.VERSION }}.${{ github.run_number }}
+ name: "Release ${{ env.VERSION }}"
+ body: |
+ Version: ${{ env.VERSION }}
+ Commit: ${{ github.sha }}
+ token: ${{ secrets.GITHUB_TOKEN }}
+ draft: false
+ prerelease: ${{ contains(env.VERSION, '-') }}
\ No newline at end of file
diff --git a/Api.Documentation.Nonce/.gitignore b/.gitignore
similarity index 100%
rename from Api.Documentation.Nonce/.gitignore
rename to .gitignore
diff --git a/Api.Documentation.Nonce/.docker/docker-compose.dcproj b/Api.ApiClients.Audit/.docker/docker-compose.dcproj
similarity index 100%
rename from Api.Documentation.Nonce/.docker/docker-compose.dcproj
rename to Api.ApiClients.Audit/.docker/docker-compose.dcproj
diff --git a/Api.ApiClients.Audit/.docker/docker-compose.yml b/Api.ApiClients.Audit/.docker/docker-compose.yml
new file mode 100644
index 00000000..3701e77a
--- /dev/null
+++ b/Api.ApiClients.Audit/.docker/docker-compose.yml
@@ -0,0 +1,44 @@
+services:
+ api.apiclients.audit:
+ image: api.apiclients.audit
+ hostname: api-apiclients-audit
+ restart: on-failure
+ ports:
+ - 8080:8080
+ build:
+ context: ../Api.ApiClients.Audit
+ dockerfile: "Dockerfile.Local"
+ networks:
+ - network
+
+ api.apiclients.audit.service:
+ image: api.apiclients.audit.service
+ hostname: api-apiclients-audit-service
+ restart: on-failure
+ ports:
+ - 8181:8181
+ build:
+ context: ../Api.ApiClients.Audit.Service
+ dockerfile: "Dockerfile.Local"
+ depends_on:
+ - database
+ networks:
+ - network
+
+ database:
+ image: mysql/mysql-server:latest
+ ports:
+ - 3306:3306
+ networks:
+ - network
+ environment:
+ MYSQL_USER: sa
+ MYSQL_PASSWORD: myPassword_123
+ MYSQL_ROOT_PASSWORD: myPassword_123
+ MYSQL_DATABASE: nanoDb
+ MYSQL_ROOT_HOST: '%'
+
+networks:
+ network:
+ name: network
+ driver: bridge
diff --git a/Api.Documentation.Nonce/.dockerignore b/Api.ApiClients.Audit/.dockerignore
similarity index 100%
rename from Api.Documentation.Nonce/.dockerignore
rename to Api.ApiClients.Audit/.dockerignore
diff --git a/Api.Documentation.Nonce/.github/config/slack.yml b/Api.ApiClients.Audit/.github/config/slack.yml
similarity index 100%
rename from Api.Documentation.Nonce/.github/config/slack.yml
rename to Api.ApiClients.Audit/.github/config/slack.yml
diff --git a/Api.Documentation.Nonce/.github/workflows/build-and-deploy.yml b/Api.ApiClients.Audit/.github/workflows/build-and-deploy.yml
similarity index 64%
rename from Api.Documentation.Nonce/.github/workflows/build-and-deploy.yml
rename to Api.ApiClients.Audit/.github/workflows/build-and-deploy.yml
index 420e282f..2e9dbcea 100644
--- a/Api.Documentation.Nonce/.github/workflows/build-and-deploy.yml
+++ b/Api.ApiClients.Audit/.github/workflows/build-and-deploy.yml
@@ -1,29 +1,32 @@
name: Build And Deploy
on:
- push
+ pull_request:
+ branches:
+ - master
+ push:
+ branches:
+ - master
env:
- APP_NAME: Api.Documentation.Nonce
- IMAGE_NAME: api.documentation.nonce
- SERVICE_NAME: api-documentation-nonce
+ APP_NAME: Api.ApiClients.Audit
+ IMAGE_NAME: api.apiclients.audit
+ SERVICE_NAME: api-apiclients-audit
VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}'
DOTNET_SDK_VERSION: 10.0
DOTNET_ASPNET_VERSION: 10.0
- AZURE_GROUP: ${{ vars.AZURE_KUBERNETES_RESOURCE_GROUP }}
+ AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }}
AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }}
AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }}
- NUGET_HOST: ${{ secrets.NUGET_HOST }}
- NUGET_USERNAME: ${{ secrets.NUGET_USERNAME }}
- NUGET_PASSWORD: ${{ secrets.NUGET_APIKEY }}
- NUGET_APIKEY: ${{ secrets.NUGET_APIKEY }}
- CONTAINER_REGISTRY_HOST: ${{ vars.CONTAINER_REGISTRY_HOST }}
- CONTAINER_REGISTRY_USERNAME: ${{ secrets.CONTAINER_REGISTRY_USERNAME }}
- CONTAINER_REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }}
+ NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json
+ NUGET_USERNAME: ${{ github.actor }}
+ NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }}
+ CONTAINER_REGISTRY_HOST: ghcr.io
+ CONTAINER_REGISTRY_USERNAME: ${{ github.actor }}
+ CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }}
CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }}
- KUBERNETES_CLUSTER: ${{ github.ref == 'refs/heads/master' && vars.PRODUCTION_KUBERNETES_CLUSTER || vars.STAGING_KUBERNETES_CLUSTER }}
KUBERNETES_NODEPOOL_COMPUTE: cpu
- KUBERNETES_NAMESPACE: default
+ KUBERNETES_NAMESPACE: apps
KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }}
KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }}
KUBERNETES_REPLICA_HISTORY_COUNT: 0
@@ -33,30 +36,31 @@ env:
KUBERNETES_CPU_REQUEST: 200m
KUBERNETES_CPU_LIMIT: 600m
KUBERNETES_CPU_SCALING: 180
- CERTIFICATE_ISSUER: letsencrypt-prod
- CERTIFICATE_ORGANIZATION: ${{ vars.CERTIFICATE_ORGANIZATION }}
- CERTIFICATE_HOST: ${{ github.ref == 'refs/heads/master' && vars.HOST_API_SUBDOMAIN + '.' + vars.PRODUCTION_HOST || vars.HOST_API_SUBDOMAIN + '.' + vars.STAGING_HOST }}
ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }}
- NONCE_TOKEN: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_NONCE_TOKEN || secrets.STAGING_NONCE_TOKEN }}
jobs:
build-and-deploy:
- runs-on: ubuntu-latest
+ runs-on:
+ - self-hosted
+ - linux
+ - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }}
permissions:
- contents: read
+ contents: write
packages: write
id-token: write
concurrency:
group: ${{ github.repository }}
cancel-in-progress: true
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v6
- name: Azure Login
shell: pwsh
run: |
- sudo az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none;
- sudo az account set -s $env:AZURE_SUBSCRIPTION_ID -o none;
- sudo az aks get-credentials -g $env:AZURE_GROUP -n $env:KUBERNETES_CLUSTER --overwrite -o none;
+ az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none;
+ az account set -s $env:AZURE_SUBSCRIPTION_ID -o none;
+
+ $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv;
+ az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none;
- name: Build
shell: pwsh
@@ -85,7 +89,7 @@ jobs:
$imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest";
$imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION
- sudo docker build `
+ docker build `
-t $imageLatestTag `
-t $imageVersionTag `
--build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION `
@@ -101,9 +105,9 @@ jobs:
throw "error";
};
- sudo docker login -u="$env:CONTAINER_REGISTRY_USERNAME" -p="$env:CONTAINER_REGISTRY_PASSWORD" $env:CONTAINER_REGISTRY_HOST;
- sudo docker push $imageLatestTag;
- sudo docker push $imageVersionTag;
+ echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin;
+ docker push $imageLatestTag;
+ docker push $imageVersionTag;
if ($LastExitCode -ne 0)
{
throw "error";
@@ -114,7 +118,7 @@ jobs:
run: |
$nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj";
dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build;
- dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_APIKEY;
+ dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD;
if ($LastExitCode -ne 0)
{
throw "error";
@@ -123,43 +127,29 @@ jobs:
- name: Kubernetes Deploy
shell: pwsh
run: |
- Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml;
- sudo kubectl apply -f .kubernetes/service.tmp.yaml;
+ Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml;
+ kubectl apply -f .kubernetes/service.tmp.yaml;
if ($LastExitCode -ne 0)
{
throw "error";
};
Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml;
- sudo kubectl apply -f .kubernetes/configmap.tmp.yaml;
+ kubectl apply -f .kubernetes/configmap.tmp.yaml;
if ($LastExitCode -ne 0)
{
throw "error";
};
Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml;
- sudo kubectl apply -f .kubernetes/deployment.tmp.yaml;
+ kubectl apply -f .kubernetes/deployment.tmp.yaml;
if ($LastExitCode -ne 0)
{
throw "error";
};
Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml;
- sudo kubectl apply -f .kubernetes/autoscaler.tmp.yaml;
- if ($LastExitCode -ne 0)
- {
- throw "error";
- };
-
- Get-Content .kubernetes/certificate.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/certificate.tmp.yaml;
- sudo kubectl apply -f .kubernetes/certificate.tmp.yaml;
- if ($LastExitCode -ne 0)
- {
- throw "error";
- };
-
- Get-Content .kubernetes/ingress.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/ingress.tmp.yaml;
- sudo kubectl apply -f .kubernetes/ingress.tmp.yaml;
+ kubectl apply -f .kubernetes/autoscaler.tmp.yaml;
if ($LastExitCode -ne 0)
{
throw "error";
diff --git a/Api.ApiClients.Audit/.gitignore b/Api.ApiClients.Audit/.gitignore
new file mode 100644
index 00000000..9921fc06
--- /dev/null
+++ b/Api.ApiClients.Audit/.gitignore
@@ -0,0 +1,11 @@
+.vs
+*.user
+*.userprefs
+*.suo
+_ReSharper*
+**/bin
+**/obj
+*.DotSettings.User
+packages
+.env
+**/Properties/launchSettings.json
\ No newline at end of file
diff --git a/Api.Documentation.Nonce/.kubernetes/autoscaler.yaml b/Api.ApiClients.Audit/.kubernetes/autoscaler.yaml
similarity index 100%
rename from Api.Documentation.Nonce/.kubernetes/autoscaler.yaml
rename to Api.ApiClients.Audit/.kubernetes/autoscaler.yaml
diff --git a/Api.Documentation.Nonce/.kubernetes/configmap.yaml b/Api.ApiClients.Audit/.kubernetes/configmap.yaml
similarity index 100%
rename from Api.Documentation.Nonce/.kubernetes/configmap.yaml
rename to Api.ApiClients.Audit/.kubernetes/configmap.yaml
diff --git a/Api.Documentation.Nonce/.kubernetes/deployment.yaml b/Api.ApiClients.Audit/.kubernetes/deployment.yaml
similarity index 91%
rename from Api.Documentation.Nonce/.kubernetes/deployment.yaml
rename to Api.ApiClients.Audit/.kubernetes/deployment.yaml
index fdc5dfdf..02882c56 100644
--- a/Api.Documentation.Nonce/.kubernetes/deployment.yaml
+++ b/Api.ApiClients.Audit/.kubernetes/deployment.yaml
@@ -20,6 +20,7 @@ spec:
securityContext:
runAsUser: 1000
runAsGroup: 2000
+ fsGroup: 2000
topologySpreadConstraints:
- maxSkew: 1
topologyKey: kubernetes.io/hostname
@@ -27,7 +28,6 @@ spec:
labelSelector:
matchLabels:
app: %SERVICE_NAME%
- automountServiceAccountToken: false
nodeSelector:
nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE%
kubernetes.io/os: linux
@@ -73,11 +73,6 @@ spec:
periodSeconds: 5
initialDelaySeconds: 20
timeoutSeconds: 2
- volumeMounts:
- - name: tmp
- mountPath: /tmp
- - name: %SERVICE_NAME%-volume
- mountPath: /mnt/%STORAGE_SHARE_NAME%
imagePullSecrets:
- name: ghcr-pull-secret
diff --git a/Api.Documentation.Nonce/.kubernetes/service.yaml b/Api.ApiClients.Audit/.kubernetes/service.yaml
similarity index 100%
rename from Api.Documentation.Nonce/.kubernetes/service.yaml
rename to Api.ApiClients.Audit/.kubernetes/service.yaml
diff --git a/Api.Documentation.Nonce/.tests/Tests.Api.Documentation.Nonce/Properties/DoNotParallelize.cs b/Api.ApiClients.Audit/.tests/Tests.Api.ApiClients.Audit/Properties/DoNotParallelize.cs
similarity index 100%
rename from Api.Documentation.Nonce/.tests/Tests.Api.Documentation.Nonce/Properties/DoNotParallelize.cs
rename to Api.ApiClients.Audit/.tests/Tests.Api.ApiClients.Audit/Properties/DoNotParallelize.cs
diff --git a/Api.ApiClients.Audit/.tests/Tests.Api.ApiClients.Audit/Tests.Api.ApiClients.Audit.csproj b/Api.ApiClients.Audit/.tests/Tests.Api.ApiClients.Audit/Tests.Api.ApiClients.Audit.csproj
new file mode 100644
index 00000000..2e895288
--- /dev/null
+++ b/Api.ApiClients.Audit/.tests/Tests.Api.ApiClients.Audit/Tests.Api.ApiClients.Audit.csproj
@@ -0,0 +1,23 @@
+
+
+
+ net10.0
+ false
+ latest
+
+
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Api.ApiClients.Audit/Api.ApiClients.Audit.Models/Api.ApiClients.Audit.Models.csproj b/Api.ApiClients.Audit/Api.ApiClients.Audit.Models/Api.ApiClients.Audit.Models.csproj
new file mode 100644
index 00000000..c33a8e9a
--- /dev/null
+++ b/Api.ApiClients.Audit/Api.ApiClients.Audit.Models/Api.ApiClients.Audit.Models.csproj
@@ -0,0 +1,75 @@
+
+
+
+ net10.0
+
+ 10.0.0.0
+ 10.0.0.0
+ 10.0.0.0
+ AnyCPU
+ Debug;Release
+ latest
+ en-US
+ enable
+ LICENSE
+ true
+
+ True
+ true
+ true
+ true
+ Michael Vivet
+ Michael Vivet
+ $(ProjectName)
+ Example project demonstrating a focused aspect of the Nano Library.
+
+ A practical example highlighting a specific area or scenario of Nano for learning
+ and experimentation. Shows how features or patterns can be applied without
+ representing a full application or complete reference.
+
+
+ - Added .NET 10 support
+
+ git
+ master
+ https://github.com/Nano-Core/Nano.Examples.git
+ $(GitCommitHash)
+ true
+ true
+ true
+ snupkg
+ true
+ $(Version)
+ nano;example;sample;framework;library;learning;showcase
+ https://github.com/Nano-Core/Nano.Library/$(ProjectName)
+ LICENSE
+ icon.png
+ README.md
+ true
+
+
+
+ True
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Api.ApiClients.Audit/Api.ApiClients.Audit.Service.Models/Api.ApiClients.Audit.Service.Models.csproj b/Api.ApiClients.Audit/Api.ApiClients.Audit.Service.Models/Api.ApiClients.Audit.Service.Models.csproj
new file mode 100644
index 00000000..82be9b94
--- /dev/null
+++ b/Api.ApiClients.Audit/Api.ApiClients.Audit.Service.Models/Api.ApiClients.Audit.Service.Models.csproj
@@ -0,0 +1,77 @@
+
+
+
+ net10.0
+
+ 10.0.0.0
+ 10.0.0.0
+ 10.0.0.0
+ AnyCPU
+ Debug;Release
+ latest
+ en-US
+ enable
+ LICENSE
+ true
+
+ True
+ true
+ true
+ true
+ Michael Vivet
+ Michael Vivet
+ $(ProjectName)
+ Example project demonstrating a focused aspect of the Nano Library.
+
+ A practical example highlighting a specific area or scenario of Nano for learning
+ and experimentation. Shows how features or patterns can be applied without
+ representing a full application or complete reference.
+
+
+ - Added .NET 10 support
+
+ git
+ master
+ https://github.com/Nano-Core/Nano.Examples.git
+ $(GitCommitHash)
+ true
+ true
+ true
+ snupkg
+ true
+ $(Version)
+ nano;example;sample;framework;library;learning;showcase
+ https://github.com/Nano-Core/Nano.Library/$(ProjectName)
+ LICENSE
+ icon.png
+ README.md
+ true
+
+
+
+ True
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Api.ApiClients.Audit/Api.ApiClients.Audit.Service.Models/ApiClient/NanoApiClient.cs b/Api.ApiClients.Audit/Api.ApiClients.Audit.Service.Models/ApiClient/NanoApiClient.cs
new file mode 100644
index 00000000..23e08224
--- /dev/null
+++ b/Api.ApiClients.Audit/Api.ApiClients.Audit.Service.Models/ApiClient/NanoApiClient.cs
@@ -0,0 +1,8 @@
+using Nano.App.ApiClient;
+
+namespace Api.ApiClients.Audit.Service.Models.ApiClient;
+
+///
+/// Nano Api Client.
+///
+public class NanoApiClient(Nano.App.ApiClient.ApiClient apiClient) : BaseApiClient(apiClient);
\ No newline at end of file
diff --git a/Api.ApiClients.Audit/Api.ApiClients.Audit.Service.Models/Criterias/ExampleQueryCriteria.cs b/Api.ApiClients.Audit/Api.ApiClients.Audit.Service.Models/Criterias/ExampleQueryCriteria.cs
new file mode 100644
index 00000000..f35de66c
--- /dev/null
+++ b/Api.ApiClients.Audit/Api.ApiClients.Audit.Service.Models/Criterias/ExampleQueryCriteria.cs
@@ -0,0 +1,34 @@
+using System.Collections.Generic;
+using System.Linq;
+using DynamicExpression;
+using Nano.App.Api.Controllers.Criteria;
+
+namespace Api.ApiClients.Audit.Service.Models.Criterias;
+
+///
+public class ExampleQueryCriteria : BaseQueryCriteria
+{
+ ///
+ /// Name.
+ ///
+ public virtual string? Name { get; set; }
+
+ ///
+ public override IList GetExpressions()
+ {
+ var expressions = base.GetExpressions();
+
+ var expression = expressions.FirstOrDefault() ?? new CriteriaExpression();
+
+ if (!string.IsNullOrEmpty(this.Name))
+ {
+ expression
+ .StartsWith(nameof(Example.Name), this.Name);
+ }
+
+ expressions
+ .Add(expression);
+
+ return expressions;
+ }
+}
\ No newline at end of file
diff --git a/Api.ApiClients.Audit/Api.ApiClients.Audit.Service.Models/Example.cs b/Api.ApiClients.Audit/Api.ApiClients.Audit.Service.Models/Example.cs
new file mode 100644
index 00000000..0ed5bd69
--- /dev/null
+++ b/Api.ApiClients.Audit/Api.ApiClients.Audit.Service.Models/Example.cs
@@ -0,0 +1,28 @@
+using System;
+using Nano.Data.Abstractions.Annotations;
+using Nano.Data.Abstractions.Models;
+using Nano.Data.Abstractions.Models.Abstractions;
+
+namespace Api.ApiClients.Audit.Service.Models;
+
+///
+/// Example.
+///
+public class Example : BaseEntity, IEntityAuditable, IEntitySoftDeletable
+{
+ ///
+ /// Name.
+ ///
+ public virtual string Name { get; set; } = null!;
+
+ ///
+ /// Navigation Id.
+ ///
+ public virtual Guid? NavigationId { get; set; }
+
+ ///
+ /// Navigation.
+ ///
+ [Include]
+ public virtual ExampleNavigation? Navigation { get; set; }
+}
\ No newline at end of file
diff --git a/Api.ApiClients.Audit/Api.ApiClients.Audit.Service.Models/ExampleNavigation.cs b/Api.ApiClients.Audit/Api.ApiClients.Audit.Service.Models/ExampleNavigation.cs
new file mode 100644
index 00000000..80fd08ef
--- /dev/null
+++ b/Api.ApiClients.Audit/Api.ApiClients.Audit.Service.Models/ExampleNavigation.cs
@@ -0,0 +1,21 @@
+using System.Collections.Generic;
+using Nano.Data.Abstractions.Models;
+using Nano.Data.Abstractions.Models.Abstractions;
+
+namespace Api.ApiClients.Audit.Service.Models;
+
+///
+/// Example Navigation.
+///
+public class ExampleNavigation : BaseEntity, IEntityAuditable, IEntitySoftDeletable
+{
+ ///
+ /// Navigation Name.
+ ///
+ public virtual string NavigationName { get; set; } = null!;
+
+ ///
+ /// Examples.
+ ///
+ public virtual ICollection Examples { get; set; } = [];
+}
\ No newline at end of file
diff --git a/Api.ApiClients.Audit/Api.ApiClients.Audit.Service/Api.ApiClients.Audit.Service.csproj b/Api.ApiClients.Audit/Api.ApiClients.Audit.Service/Api.ApiClients.Audit.Service.csproj
new file mode 100644
index 00000000..ffe64f93
--- /dev/null
+++ b/Api.ApiClients.Audit/Api.ApiClients.Audit.Service/Api.ApiClients.Audit.Service.csproj
@@ -0,0 +1,37 @@
+
+
+
+ net10.0
+
+ 10.0.0.0
+ 10.0.0.0
+ 10.0.0.0
+ AnyCPU
+ Debug;Release
+ latest
+ en-US
+ enable
+ LICENSE
+ true
+
+ false
+ true
+ false
+ true
+ Linux
+ ..\docker-compose.dcproj
+
+
+
+ True
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Api.ApiClients.Audit/Api.ApiClients.Audit.Service/Controllers/AuditController.cs b/Api.ApiClients.Audit/Api.ApiClients.Audit.Service/Controllers/AuditController.cs
new file mode 100644
index 00000000..bd90f558
--- /dev/null
+++ b/Api.ApiClients.Audit/Api.ApiClients.Audit.Service/Controllers/AuditController.cs
@@ -0,0 +1,9 @@
+using Microsoft.Extensions.Logging;
+using Nano.App.Api.Controllers;
+using Nano.Data.Abstractions;
+
+namespace Api.ApiClients.Audit.Service.Controllers;
+
+///
+public class AuditController(ILogger logger, IRepository repository)
+ : BaseAuditController(logger, repository);
\ No newline at end of file
diff --git a/Api.ApiClients.Audit/Api.ApiClients.Audit.Service/Controllers/ExamplesController.cs b/Api.ApiClients.Audit/Api.ApiClients.Audit.Service/Controllers/ExamplesController.cs
new file mode 100644
index 00000000..e5e8c595
--- /dev/null
+++ b/Api.ApiClients.Audit/Api.ApiClients.Audit.Service/Controllers/ExamplesController.cs
@@ -0,0 +1,15 @@
+using Api.ApiClients.Audit.Service.Models;
+using Api.ApiClients.Audit.Service.Models.Criterias;
+using Microsoft.Extensions.Logging;
+using Nano.App.Api.Controllers;
+using Nano.Data.Abstractions;
+
+namespace Api.ApiClients.Audit.Service.Controllers;
+
+///
+/// Controller with examples.
+///
+/// The .
+/// The .
+public class ExamplesController(ILogger logger, IRepository repository)
+ : BaseEntityController(logger, repository);
\ No newline at end of file
diff --git a/Api.ApiClients.Audit/Api.ApiClients.Audit.Service/Data/Mappings/ExampleMapping.cs b/Api.ApiClients.Audit/Api.ApiClients.Audit.Service/Data/Mappings/ExampleMapping.cs
new file mode 100644
index 00000000..8b61ff28
--- /dev/null
+++ b/Api.ApiClients.Audit/Api.ApiClients.Audit.Service/Data/Mappings/ExampleMapping.cs
@@ -0,0 +1,34 @@
+using System;
+using Api.ApiClients.Audit.Service.Models;
+using Microsoft.EntityFrameworkCore.Metadata.Builders;
+using Nano.Data.Mappings;
+using Nano.Data.Mappings.Extensions;
+
+namespace Api.ApiClients.Audit.Service.Data.Mappings;
+
+///
+/// Example Mapping.
+///
+public class ExampleMapping : BaseEntityMapping
+{
+ ///
+ public override void Configure(EntityTypeBuilder builder)
+ {
+ ArgumentNullException.ThrowIfNull(builder);
+
+ base.Configure(builder);
+
+ builder
+ .Property(x => x.Name);
+
+ builder
+ .HasOne(x => x.Navigation)
+ .WithMany(x => x.Examples);
+
+ builder
+ .OnUpdating(x =>
+ {
+ x.Entity.Name += "-triggered";
+ });
+ }
+}
\ No newline at end of file
diff --git a/Api.ApiClients.Audit/Api.ApiClients.Audit.Service/Data/Mappings/ExampleNavigationMapping.cs b/Api.ApiClients.Audit/Api.ApiClients.Audit.Service/Data/Mappings/ExampleNavigationMapping.cs
new file mode 100644
index 00000000..f9269c03
--- /dev/null
+++ b/Api.ApiClients.Audit/Api.ApiClients.Audit.Service/Data/Mappings/ExampleNavigationMapping.cs
@@ -0,0 +1,27 @@
+using System;
+using Api.ApiClients.Audit.Service.Models;
+using Microsoft.EntityFrameworkCore.Metadata.Builders;
+using Nano.Data.Mappings;
+
+namespace Api.ApiClients.Audit.Service.Data.Mappings;
+
+///
+/// Example Navigation Mapping.
+///
+public class ExampleNavigationMapping : BaseEntityMapping
+{
+ ///
+ public override void Configure(EntityTypeBuilder builder)
+ {
+ ArgumentNullException.ThrowIfNull(builder);
+
+ base.Configure(builder);
+
+ builder
+ .Property(x => x.NavigationName);
+
+ builder
+ .HasMany(x => x.Examples)
+ .WithOne(x => x.Navigation);
+ }
+}
\ No newline at end of file
diff --git a/Api.ApiClients.Audit/Api.ApiClients.Audit.Service/Data/MySqlDbContext.cs b/Api.ApiClients.Audit/Api.ApiClients.Audit.Service/Data/MySqlDbContext.cs
new file mode 100644
index 00000000..3dfa9c4a
--- /dev/null
+++ b/Api.ApiClients.Audit/Api.ApiClients.Audit.Service/Data/MySqlDbContext.cs
@@ -0,0 +1,10 @@
+using Microsoft.EntityFrameworkCore;
+using Microsoft.Extensions.Options;
+using Nano.Data;
+using Nano.Data.Abstractions.Config;
+
+namespace Api.ApiClients.Audit.Service.Data;
+
+///
+public class MySqlDbContext(DbContextOptions contextOptions, IOptionsMonitor dataOptions)
+ : BaseDbContext(contextOptions, dataOptions);
\ No newline at end of file
diff --git a/Api.ApiClients.Audit/Api.ApiClients.Audit.Service/Data/MySqlDbContextFactory.cs b/Api.ApiClients.Audit/Api.ApiClients.Audit.Service/Data/MySqlDbContextFactory.cs
new file mode 100644
index 00000000..4850809c
--- /dev/null
+++ b/Api.ApiClients.Audit/Api.ApiClients.Audit.Service/Data/MySqlDbContextFactory.cs
@@ -0,0 +1,7 @@
+using Nano.Data;
+using Nano.Data.MySql;
+
+namespace Api.ApiClients.Audit.Service.Data;
+
+///
+public class MySqlDbContextFactory : BaseDbContextFactory;
\ No newline at end of file
diff --git a/Api.ApiClients.Audit/Api.ApiClients.Audit.Service/Dockerfile.Local b/Api.ApiClients.Audit/Api.ApiClients.Audit.Service/Dockerfile.Local
new file mode 100644
index 00000000..1e6d12c6
--- /dev/null
+++ b/Api.ApiClients.Audit/Api.ApiClients.Audit.Service/Dockerfile.Local
@@ -0,0 +1,6 @@
+ARG DOTNET_ASPNET_VERSION="10.0"
+FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base
+
+EXPOSE 8080
+
+ENTRYPOINT ["dotnet", "Api.ApiClients.Audit.Service.dll"]
\ No newline at end of file
diff --git a/Api.ApiClients.Audit/Api.ApiClients.Audit.Service/Migrations/20260415133755_Initial.Designer.cs b/Api.ApiClients.Audit/Api.ApiClients.Audit.Service/Migrations/20260415133755_Initial.Designer.cs
new file mode 100644
index 00000000..077c87f2
--- /dev/null
+++ b/Api.ApiClients.Audit/Api.ApiClients.Audit.Service/Migrations/20260415133755_Initial.Designer.cs
@@ -0,0 +1,728 @@
+//
+using System;
+using Api.ApiClients.Audit.Service.Data;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Metadata;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+
+#nullable disable
+
+namespace Api.Data.Audit.Migrations
+{
+ [DbContext(typeof(MySqlDbContext))]
+ [Migration("20260415133755_Initial")]
+ partial class Initial
+ {
+ ///
+ protected override void BuildTargetModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder
+ .HasAnnotation("ProductVersion", "9.0.11")
+ .HasAnnotation("Relational:MaxIdentifierLength", 64);
+
+ MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder);
+
+ modelBuilder.Entity("Api.Data.Audit.Models.Example", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("char(36)");
+
+ b.Property("CreatedAt")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("datetime(6)");
+
+ MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt"));
+
+ b.Property("IsDeleted")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("bigint")
+ .HasDefaultValue(0L);
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.Property("NavigationId")
+ .HasColumnType("char(36)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("CreatedAt");
+
+ b.HasIndex("IsDeleted");
+
+ b.HasIndex("NavigationId");
+
+ b.ToTable("Example");
+ });
+
+ modelBuilder.Entity("Api.Data.Audit.Models.ExampleNavigation", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("char(36)");
+
+ b.Property("CreatedAt")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("datetime(6)");
+
+ MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt"));
+
+ b.Property("IsDeleted")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("bigint")
+ .HasDefaultValue(0L);
+
+ b.Property("NavigationName")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.HasKey("Id");
+
+ b.HasIndex("CreatedAt");
+
+ b.HasIndex("IsDeleted");
+
+ b.ToTable("ExampleNavigation");
+ });
+
+ modelBuilder.Entity("Api.Data.Audit.Models.ExampleNoAudit", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("char(36)");
+
+ b.Property("CreatedAt")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("datetime(6)");
+
+ MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt"));
+
+ b.Property("IsDeleted")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("bigint")
+ .HasDefaultValue(0L);
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.HasKey("Id");
+
+ b.HasIndex("CreatedAt");
+
+ b.HasIndex("IsDeleted");
+
+ b.ToTable("ExampleNoAudit");
+ });
+
+ modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id"));
+
+ b.Property("FriendlyName")
+ .HasColumnType("longtext");
+
+ b.Property("Xml")
+ .HasColumnType("longtext");
+
+ b.HasKey("Id");
+
+ b.ToTable("__EFDataProtectionKeys", (string)null);
+ });
+
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("char(36)");
+
+ b.Property("ConcurrencyStamp")
+ .IsConcurrencyToken()
+ .HasColumnType("longtext");
+
+ b.Property("Name")
+ .HasMaxLength(256)
+ .HasColumnType("varchar(256)");
+
+ b.Property("NormalizedName")
+ .HasMaxLength(256)
+ .HasColumnType("varchar(256)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("NormalizedName")
+ .IsUnique()
+ .HasDatabaseName("RoleNameIndex");
+
+ b.ToTable("__EFIdentityRole", (string)null);
+ });
+
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id"));
+
+ b.Property("ClaimType")
+ .HasColumnType("longtext");
+
+ b.Property("ClaimValue")
+ .HasColumnType("longtext");
+
+ b.Property("RoleId")
+ .HasColumnType("char(36)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("RoleId");
+
+ b.ToTable("__EFIdentityRoleClaim", (string)null);
+ });
+
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id"));
+
+ b.Property("ClaimType")
+ .HasColumnType("longtext");
+
+ b.Property("ClaimValue")
+ .HasColumnType("longtext");
+
+ b.Property("UserId")
+ .HasColumnType("char(36)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("UserId");
+
+ b.ToTable("__EFIdentityUserClaim", (string)null);
+ });
+
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b =>
+ {
+ b.Property("LoginProvider")
+ .HasColumnType("varchar(255)");
+
+ b.Property("ProviderKey")
+ .HasColumnType("varchar(255)");
+
+ b.Property("ProviderDisplayName")
+ .HasColumnType("longtext");
+
+ b.Property("UserId")
+ .HasColumnType("char(36)");
+
+ b.HasKey("LoginProvider", "ProviderKey");
+
+ b.HasIndex("UserId");
+
+ b.ToTable("__EFIdentityUserLogin", (string)null);
+ });
+
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b =>
+ {
+ b.Property("UserId")
+ .HasColumnType("char(36)");
+
+ b.Property("RoleId")
+ .HasColumnType("char(36)");
+
+ b.HasKey("UserId", "RoleId");
+
+ b.HasIndex("RoleId");
+
+ b.ToTable("__EFIdentityUserRole", (string)null);
+ });
+
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b =>
+ {
+ b.Property("UserId")
+ .HasColumnType("char(36)");
+
+ b.Property("LoginProvider")
+ .HasColumnType("varchar(255)");
+
+ b.Property("Name")
+ .HasColumnType("varchar(255)");
+
+ b.Property("Value")
+ .HasColumnType("longtext");
+
+ b.HasKey("UserId", "LoginProvider", "Name");
+
+ b.ToTable("__EFIdentityUserToken", (string)null);
+ });
+
+ modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("char(36)");
+
+ b.Property("CreatedAt")
+ .HasColumnType("datetime(6)");
+
+ b.Property("CreatedBy")
+ .IsRequired()
+ .HasMaxLength(256)
+ .HasColumnType("varchar(256)");
+
+ b.Property("EntityKey")
+ .HasColumnType("char(36)");
+
+ b.Property("EntitySetName")
+ .HasMaxLength(256)
+ .HasColumnType("varchar(256)");
+
+ b.Property("EntityState")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int")
+ .HasDefaultValue(0);
+
+ b.Property("EntityTypeName")
+ .IsRequired()
+ .HasMaxLength(256)
+ .HasColumnType("varchar(256)");
+
+ b.Property("IsDeleted")
+ .HasColumnType("bigint");
+
+ b.Property("RequestId")
+ .HasMaxLength(256)
+ .HasColumnType("varchar(256)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("CreatedBy");
+
+ b.HasIndex("EntityKey");
+
+ b.HasIndex("EntityState");
+
+ b.HasIndex("EntityTypeName");
+
+ b.HasIndex("RequestId");
+
+ b.ToTable("__EFAudit", (string)null);
+ });
+
+ modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("char(36)");
+
+ b.Property("CreatedAt")
+ .HasColumnType("datetime(6)");
+
+ b.Property("IsDeleted")
+ .HasColumnType("bigint");
+
+ b.Property("NewValue")
+ .HasColumnType("longtext");
+
+ b.Property("OldValue")
+ .HasColumnType("longtext");
+
+ b.Property("ParentId")
+ .HasColumnType("char(36)");
+
+ b.Property("PropertyName")
+ .IsRequired()
+ .HasMaxLength(256)
+ .HasColumnType("varchar(256)");
+
+ b.Property("RelationName")
+ .HasMaxLength(256)
+ .HasColumnType("varchar(256)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("ParentId");
+
+ b.HasIndex("PropertyName");
+
+ b.ToTable("__EFAuditProperties", (string)null);
+ });
+
+ modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("char(36)");
+
+ b.Property("CreatedAt")
+ .HasColumnType("datetime(6)");
+
+ b.Property("Hash")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.Property("IdentityUserId")
+ .HasColumnType("char(36)");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasMaxLength(256)
+ .HasColumnType("varchar(256)");
+
+ b.Property("RevokedAt")
+ .HasColumnType("datetime(6)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("IdentityUserId");
+
+ b.HasIndex("RevokedAt");
+
+ b.ToTable("__EFIdentityApiKey", (string)null);
+ });
+
+ modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("char(36)");
+
+ b.Property("ApiKeyId")
+ .HasColumnType("char(36)");
+
+ b.Property("ClaimType")
+ .IsRequired()
+ .HasColumnType("varchar(255)");
+
+ b.Property("ClaimValue")
+ .HasColumnType("longtext");
+
+ b.HasKey("Id");
+
+ b.HasIndex("ApiKeyId", "ClaimType")
+ .IsUnique()
+ .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType");
+
+ b.ToTable("__EFIdentityApiKeyClaim", (string)null);
+ });
+
+ modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("char(36)");
+
+ b.Property("ApiKeyId")
+ .HasColumnType("char(36)");
+
+ b.Property("RoleId")
+ .HasColumnType("char(36)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("RoleId");
+
+ b.HasIndex("ApiKeyId", "RoleId")
+ .IsUnique()
+ .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId");
+
+ b.ToTable("__EFIdentityApiKeyRole", (string)null);
+ });
+
+ modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("char(36)");
+
+ b.Property("IdentityUserId")
+ .HasColumnType("char(36)");
+
+ b.Property("NewEmail")
+ .HasMaxLength(256)
+ .HasColumnType("varchar(256)");
+
+ b.Property("NewPhoneNumber")
+ .HasMaxLength(20)
+ .HasColumnType("varchar(20)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("IdentityUserId")
+ .IsUnique()
+ .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId");
+
+ b.ToTable("__EFIdentityUserChangeData", (string)null);
+ });
+
+ modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("char(36)");
+
+ b.Property("AccessFailedCount")
+ .HasColumnType("int");
+
+ b.Property("ConcurrencyStamp")
+ .IsConcurrencyToken()
+ .HasColumnType("longtext");
+
+ b.Property("Email")
+ .HasMaxLength(256)
+ .HasColumnType("varchar(256)");
+
+ b.Property("EmailConfirmed")
+ .HasColumnType("tinyint(1)");
+
+ b.Property("IsActive")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("tinyint(1)")
+ .HasDefaultValue(true);
+
+ b.Property("LockoutEnabled")
+ .HasColumnType("tinyint(1)");
+
+ b.Property("LockoutEnd")
+ .HasColumnType("datetime(6)");
+
+ b.Property("NormalizedEmail")
+ .HasMaxLength(256)
+ .HasColumnType("varchar(256)");
+
+ b.Property("NormalizedUserName")
+ .HasMaxLength(256)
+ .HasColumnType("varchar(256)");
+
+ b.Property("PasswordHash")
+ .HasColumnType("longtext");
+
+ b.Property("PhoneNumber")
+ .HasColumnType("varchar(255)");
+
+ b.Property("PhoneNumberConfirmed")
+ .HasColumnType("tinyint(1)");
+
+ b.Property("SecurityStamp")
+ .HasColumnType("longtext");
+
+ b.Property("TwoFactorEnabled")
+ .HasColumnType("tinyint(1)");
+
+ b.Property("UserName")
+ .HasMaxLength(256)
+ .HasColumnType("varchar(256)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("Email")
+ .IsUnique();
+
+ b.HasIndex("IsActive");
+
+ b.HasIndex("NormalizedEmail")
+ .HasDatabaseName("EmailIndex");
+
+ b.HasIndex("NormalizedUserName")
+ .IsUnique()
+ .HasDatabaseName("UserNameIndex");
+
+ b.HasIndex("PhoneNumber")
+ .IsUnique();
+
+ b.ToTable("__EFIdentityUser", (string)null);
+ });
+
+ modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("char(36)");
+
+ b.Property("AppId")
+ .IsRequired()
+ .HasMaxLength(256)
+ .HasColumnType("varchar(256)");
+
+ b.Property("ExpireAt")
+ .HasColumnType("datetime(6)");
+
+ b.Property("IdentityUserId")
+ .HasColumnType("char(36)");
+
+ b.Property("Value")
+ .IsRequired()
+ .HasMaxLength(256)
+ .HasColumnType("varchar(256)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("ExpireAt");
+
+ b.HasIndex("IdentityUserId", "AppId")
+ .IsUnique()
+ .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId");
+
+ b.ToTable("__EFIdentityUserRefreshToken", (string)null);
+ });
+
+ modelBuilder.Entity("Api.Data.Audit.Models.Example", b =>
+ {
+ b.HasOne("Api.Data.Audit.Models.ExampleNavigation", "Navigation")
+ .WithMany("Examples")
+ .HasForeignKey("NavigationId");
+
+ b.Navigation("Navigation");
+ });
+
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b =>
+ {
+ b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
+ .WithMany()
+ .HasForeignKey("RoleId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b =>
+ {
+ b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null)
+ .WithMany()
+ .HasForeignKey("UserId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b =>
+ {
+ b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null)
+ .WithMany()
+ .HasForeignKey("UserId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b =>
+ {
+ b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
+ .WithMany()
+ .HasForeignKey("RoleId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null)
+ .WithMany()
+ .HasForeignKey("UserId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b =>
+ {
+ b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null)
+ .WithMany()
+ .HasForeignKey("UserId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b =>
+ {
+ b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent")
+ .WithMany("Properties")
+ .HasForeignKey("ParentId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("Parent");
+ });
+
+ modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b =>
+ {
+ b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser")
+ .WithMany()
+ .HasForeignKey("IdentityUserId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("IdentityUser");
+ });
+
+ modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b =>
+ {
+ b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey")
+ .WithMany()
+ .HasForeignKey("ApiKeyId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("ApiKey");
+ });
+
+ modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b =>
+ {
+ b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey")
+ .WithMany()
+ .HasForeignKey("ApiKeyId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role")
+ .WithMany()
+ .HasForeignKey("RoleId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("ApiKey");
+
+ b.Navigation("Role");
+ });
+
+ modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b =>
+ {
+ b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser")
+ .WithOne()
+ .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("IdentityUser");
+ });
+
+ modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b =>
+ {
+ b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser")
+ .WithMany()
+ .HasForeignKey("IdentityUserId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("IdentityUser");
+ });
+
+ modelBuilder.Entity("Api.Data.Audit.Models.ExampleNavigation", b =>
+ {
+ b.Navigation("Examples");
+ });
+
+ modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b =>
+ {
+ b.Navigation("Properties");
+ });
+#pragma warning restore 612, 618
+ }
+ }
+}
diff --git a/Api.ApiClients.Audit/Api.ApiClients.Audit.Service/Migrations/20260415133755_Initial.cs b/Api.ApiClients.Audit/Api.ApiClients.Audit.Service/Migrations/20260415133755_Initial.cs
new file mode 100644
index 00000000..dade123c
--- /dev/null
+++ b/Api.ApiClients.Audit/Api.ApiClients.Audit.Service/Migrations/20260415133755_Initial.cs
@@ -0,0 +1,667 @@
+using System;
+using Microsoft.EntityFrameworkCore.Metadata;
+using Microsoft.EntityFrameworkCore.Migrations;
+
+#nullable disable
+
+namespace Api.Data.Audit.Migrations
+{
+ ///
+ public partial class Initial : Migration
+ {
+ ///
+ protected override void Up(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.AlterDatabase()
+ .Annotation("MySql:CharSet", "utf8mb4");
+
+ migrationBuilder.CreateTable(
+ name: "__EFAudit",
+ columns: table => new
+ {
+ Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
+ CreatedBy = table.Column(type: "varchar(256)", maxLength: 256, nullable: false)
+ .Annotation("MySql:CharSet", "utf8mb4"),
+ EntityKey = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
+ EntitySetName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true)
+ .Annotation("MySql:CharSet", "utf8mb4"),
+ EntityTypeName = table.Column(type: "varchar(256)", maxLength: 256, nullable: false)
+ .Annotation("MySql:CharSet", "utf8mb4"),
+ EntityState = table.Column(type: "int", nullable: false, defaultValue: 0),
+ RequestId = table.Column(type: "varchar(256)", maxLength: 256, nullable: true)
+ .Annotation("MySql:CharSet", "utf8mb4"),
+ IsDeleted = table.Column(type: "bigint", nullable: false),
+ CreatedAt = table.Column(type: "datetime(6)", nullable: false)
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK___EFAudit", x => x.Id);
+ })
+ .Annotation("MySql:CharSet", "utf8mb4");
+
+ migrationBuilder.CreateTable(
+ name: "__EFDataProtectionKeys",
+ columns: table => new
+ {
+ Id = table.Column(type: "int", nullable: false)
+ .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
+ FriendlyName = table.Column(type: "longtext", nullable: true)
+ .Annotation("MySql:CharSet", "utf8mb4"),
+ Xml = table.Column(type: "longtext", nullable: true)
+ .Annotation("MySql:CharSet", "utf8mb4")
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK___EFDataProtectionKeys", x => x.Id);
+ })
+ .Annotation("MySql:CharSet", "utf8mb4");
+
+ migrationBuilder.CreateTable(
+ name: "__EFIdentityRole",
+ columns: table => new
+ {
+ Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
+ Name = table.Column(type: "varchar(256)", maxLength: 256, nullable: true)
+ .Annotation("MySql:CharSet", "utf8mb4"),
+ NormalizedName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true)
+ .Annotation("MySql:CharSet", "utf8mb4"),
+ ConcurrencyStamp = table.Column(type: "longtext", nullable: true)
+ .Annotation("MySql:CharSet", "utf8mb4")
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK___EFIdentityRole", x => x.Id);
+ })
+ .Annotation("MySql:CharSet", "utf8mb4");
+
+ migrationBuilder.CreateTable(
+ name: "__EFIdentityUser",
+ columns: table => new
+ {
+ Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
+ IsActive = table.Column(type: "tinyint(1)", nullable: false, defaultValue: true),
+ UserName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true)
+ .Annotation("MySql:CharSet", "utf8mb4"),
+ NormalizedUserName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true)
+ .Annotation("MySql:CharSet", "utf8mb4"),
+ Email = table.Column(type: "varchar(256)", maxLength: 256, nullable: true)
+ .Annotation("MySql:CharSet", "utf8mb4"),
+ NormalizedEmail = table.Column(type: "varchar(256)", maxLength: 256, nullable: true)
+ .Annotation("MySql:CharSet", "utf8mb4"),
+ EmailConfirmed = table.Column(type: "tinyint(1)", nullable: false),
+ PasswordHash = table.Column(type: "longtext", nullable: true)
+ .Annotation("MySql:CharSet", "utf8mb4"),
+ SecurityStamp = table.Column(type: "longtext", nullable: true)
+ .Annotation("MySql:CharSet", "utf8mb4"),
+ ConcurrencyStamp = table.Column(type: "longtext", nullable: true)
+ .Annotation("MySql:CharSet", "utf8mb4"),
+ PhoneNumber = table.Column(type: "varchar(255)", nullable: true)
+ .Annotation("MySql:CharSet", "utf8mb4"),
+ PhoneNumberConfirmed = table.Column(type: "tinyint(1)", nullable: false),
+ TwoFactorEnabled = table.Column(type: "tinyint(1)", nullable: false),
+ LockoutEnd = table.Column(type: "datetime(6)", nullable: true),
+ LockoutEnabled = table.Column(type: "tinyint(1)", nullable: false),
+ AccessFailedCount = table.Column(type: "int", nullable: false)
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK___EFIdentityUser", x => x.Id);
+ })
+ .Annotation("MySql:CharSet", "utf8mb4");
+
+ migrationBuilder.CreateTable(
+ name: "ExampleNavigation",
+ columns: table => new
+ {
+ Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
+ NavigationName = table.Column(type: "longtext", nullable: false)
+ .Annotation("MySql:CharSet", "utf8mb4"),
+ IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L),
+ CreatedAt = table.Column(type: "datetime(6)", nullable: false)
+ .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn)
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK_ExampleNavigation", x => x.Id);
+ })
+ .Annotation("MySql:CharSet", "utf8mb4");
+
+ migrationBuilder.CreateTable(
+ name: "ExampleNoAudit",
+ columns: table => new
+ {
+ Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
+ Name = table.Column(type: "longtext", nullable: false)
+ .Annotation("MySql:CharSet", "utf8mb4"),
+ IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L),
+ CreatedAt = table.Column(type: "datetime(6)", nullable: false)
+ .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn)
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK_ExampleNoAudit", x => x.Id);
+ })
+ .Annotation("MySql:CharSet", "utf8mb4");
+
+ migrationBuilder.CreateTable(
+ name: "__EFAuditProperties",
+ columns: table => new
+ {
+ Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
+ ParentId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
+ PropertyName = table.Column(type: "varchar(256)", maxLength: 256, nullable: false)
+ .Annotation("MySql:CharSet", "utf8mb4"),
+ RelationName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true)
+ .Annotation("MySql:CharSet", "utf8mb4"),
+ NewValue = table.Column(type: "longtext", nullable: true)
+ .Annotation("MySql:CharSet", "utf8mb4"),
+ OldValue = table.Column(type: "longtext", nullable: true)
+ .Annotation("MySql:CharSet", "utf8mb4"),
+ IsDeleted = table.Column(type: "bigint", nullable: false),
+ CreatedAt = table.Column(type: "datetime(6)", nullable: false)
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK___EFAuditProperties", x => x.Id);
+ table.ForeignKey(
+ name: "FK___EFAuditProperties___EFAudit_ParentId",
+ column: x => x.ParentId,
+ principalTable: "__EFAudit",
+ principalColumn: "Id",
+ onDelete: ReferentialAction.Cascade);
+ })
+ .Annotation("MySql:CharSet", "utf8mb4");
+
+ migrationBuilder.CreateTable(
+ name: "__EFIdentityRoleClaim",
+ columns: table => new
+ {
+ Id = table.Column(type: "int", nullable: false)
+ .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
+ RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
+ ClaimType = table.Column(type: "longtext", nullable: true)
+ .Annotation("MySql:CharSet", "utf8mb4"),
+ ClaimValue = table.Column(type: "longtext", nullable: true)
+ .Annotation("MySql:CharSet", "utf8mb4")
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK___EFIdentityRoleClaim", x => x.Id);
+ table.ForeignKey(
+ name: "FK___EFIdentityRoleClaim___EFIdentityRole_RoleId",
+ column: x => x.RoleId,
+ principalTable: "__EFIdentityRole",
+ principalColumn: "Id",
+ onDelete: ReferentialAction.Cascade);
+ })
+ .Annotation("MySql:CharSet", "utf8mb4");
+
+ migrationBuilder.CreateTable(
+ name: "__EFIdentityApiKey",
+ columns: table => new
+ {
+ Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
+ IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
+ Name = table.Column(type: "varchar(256)", maxLength: 256, nullable: false)
+ .Annotation("MySql:CharSet", "utf8mb4"),
+ Hash = table.Column(type: "longtext", nullable: false)
+ .Annotation("MySql:CharSet", "utf8mb4"),
+ CreatedAt = table.Column(type: "datetime(6)", nullable: false),
+ RevokedAt = table.Column(type: "datetime(6)", nullable: true)
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK___EFIdentityApiKey", x => x.Id);
+ table.ForeignKey(
+ name: "FK___EFIdentityApiKey___EFIdentityUser_IdentityUserId",
+ column: x => x.IdentityUserId,
+ principalTable: "__EFIdentityUser",
+ principalColumn: "Id",
+ onDelete: ReferentialAction.Cascade);
+ })
+ .Annotation("MySql:CharSet", "utf8mb4");
+
+ migrationBuilder.CreateTable(
+ name: "__EFIdentityUserChangeData",
+ columns: table => new
+ {
+ Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
+ IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
+ NewEmail = table.Column(type: "varchar(256)", maxLength: 256, nullable: true)
+ .Annotation("MySql:CharSet", "utf8mb4"),
+ NewPhoneNumber = table.Column(type: "varchar(20)", maxLength: 20, nullable: true)
+ .Annotation("MySql:CharSet", "utf8mb4")
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK___EFIdentityUserChangeData", x => x.Id);
+ table.ForeignKey(
+ name: "FK___EFIdentityUserChangeData___EFIdentityUser_IdentityUserId",
+ column: x => x.IdentityUserId,
+ principalTable: "__EFIdentityUser",
+ principalColumn: "Id",
+ onDelete: ReferentialAction.Cascade);
+ })
+ .Annotation("MySql:CharSet", "utf8mb4");
+
+ migrationBuilder.CreateTable(
+ name: "__EFIdentityUserClaim",
+ columns: table => new
+ {
+ Id = table.Column(type: "int", nullable: false)
+ .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
+ UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
+ ClaimType = table.Column(type: "longtext", nullable: true)
+ .Annotation("MySql:CharSet", "utf8mb4"),
+ ClaimValue = table.Column(type: "longtext", nullable: true)
+ .Annotation("MySql:CharSet", "utf8mb4")
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK___EFIdentityUserClaim", x => x.Id);
+ table.ForeignKey(
+ name: "FK___EFIdentityUserClaim___EFIdentityUser_UserId",
+ column: x => x.UserId,
+ principalTable: "__EFIdentityUser",
+ principalColumn: "Id",
+ onDelete: ReferentialAction.Cascade);
+ })
+ .Annotation("MySql:CharSet", "utf8mb4");
+
+ migrationBuilder.CreateTable(
+ name: "__EFIdentityUserLogin",
+ columns: table => new
+ {
+ LoginProvider = table.Column(type: "varchar(255)", nullable: false)
+ .Annotation("MySql:CharSet", "utf8mb4"),
+ ProviderKey = table.Column(type: "varchar(255)", nullable: false)
+ .Annotation("MySql:CharSet", "utf8mb4"),
+ ProviderDisplayName = table.Column(type: "longtext", nullable: true)
+ .Annotation("MySql:CharSet", "utf8mb4"),
+ UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci")
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK___EFIdentityUserLogin", x => new { x.LoginProvider, x.ProviderKey });
+ table.ForeignKey(
+ name: "FK___EFIdentityUserLogin___EFIdentityUser_UserId",
+ column: x => x.UserId,
+ principalTable: "__EFIdentityUser",
+ principalColumn: "Id",
+ onDelete: ReferentialAction.Cascade);
+ })
+ .Annotation("MySql:CharSet", "utf8mb4");
+
+ migrationBuilder.CreateTable(
+ name: "__EFIdentityUserRefreshToken",
+ columns: table => new
+ {
+ Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
+ IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
+ AppId = table.Column(type: "varchar(256)", maxLength: 256, nullable: false)
+ .Annotation("MySql:CharSet", "utf8mb4"),
+ Value = table.Column(type: "varchar(256)", maxLength: 256, nullable: false)
+ .Annotation("MySql:CharSet", "utf8mb4"),
+ ExpireAt = table.Column(type: "datetime(6)", nullable: true)
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK___EFIdentityUserRefreshToken", x => x.Id);
+ table.ForeignKey(
+ name: "FK___EFIdentityUserRefreshToken___EFIdentityUser_IdentityUserId",
+ column: x => x.IdentityUserId,
+ principalTable: "__EFIdentityUser",
+ principalColumn: "Id",
+ onDelete: ReferentialAction.Cascade);
+ })
+ .Annotation("MySql:CharSet", "utf8mb4");
+
+ migrationBuilder.CreateTable(
+ name: "__EFIdentityUserRole",
+ columns: table => new
+ {
+ UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
+ RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci")
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK___EFIdentityUserRole", x => new { x.UserId, x.RoleId });
+ table.ForeignKey(
+ name: "FK___EFIdentityUserRole___EFIdentityRole_RoleId",
+ column: x => x.RoleId,
+ principalTable: "__EFIdentityRole",
+ principalColumn: "Id",
+ onDelete: ReferentialAction.Cascade);
+ table.ForeignKey(
+ name: "FK___EFIdentityUserRole___EFIdentityUser_UserId",
+ column: x => x.UserId,
+ principalTable: "__EFIdentityUser",
+ principalColumn: "Id",
+ onDelete: ReferentialAction.Cascade);
+ })
+ .Annotation("MySql:CharSet", "utf8mb4");
+
+ migrationBuilder.CreateTable(
+ name: "__EFIdentityUserToken",
+ columns: table => new
+ {
+ UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
+ LoginProvider = table.Column(type: "varchar(255)", nullable: false)
+ .Annotation("MySql:CharSet", "utf8mb4"),
+ Name = table.Column(type: "varchar(255)", nullable: false)
+ .Annotation("MySql:CharSet", "utf8mb4"),
+ Value = table.Column(type: "longtext", nullable: true)
+ .Annotation("MySql:CharSet", "utf8mb4")
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK___EFIdentityUserToken", x => new { x.UserId, x.LoginProvider, x.Name });
+ table.ForeignKey(
+ name: "FK___EFIdentityUserToken___EFIdentityUser_UserId",
+ column: x => x.UserId,
+ principalTable: "__EFIdentityUser",
+ principalColumn: "Id",
+ onDelete: ReferentialAction.Cascade);
+ })
+ .Annotation("MySql:CharSet", "utf8mb4");
+
+ migrationBuilder.CreateTable(
+ name: "Example",
+ columns: table => new
+ {
+ Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
+ Name = table.Column(type: "longtext", nullable: false)
+ .Annotation("MySql:CharSet", "utf8mb4"),
+ NavigationId = table.Column(type: "char(36)", nullable: true, collation: "ascii_general_ci"),
+ IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L),
+ CreatedAt = table.Column(type: "datetime(6)", nullable: false)
+ .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn)
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK_Example", x => x.Id);
+ table.ForeignKey(
+ name: "FK_Example_ExampleNavigation_NavigationId",
+ column: x => x.NavigationId,
+ principalTable: "ExampleNavigation",
+ principalColumn: "Id");
+ })
+ .Annotation("MySql:CharSet", "utf8mb4");
+
+ migrationBuilder.CreateTable(
+ name: "__EFIdentityApiKeyClaim",
+ columns: table => new
+ {
+ Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
+ ApiKeyId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
+ ClaimType = table.Column(type: "varchar(255)", nullable: false)
+ .Annotation("MySql:CharSet", "utf8mb4"),
+ ClaimValue = table.Column(type: "longtext", nullable: true)
+ .Annotation("MySql:CharSet", "utf8mb4")
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK___EFIdentityApiKeyClaim", x => x.Id);
+ table.ForeignKey(
+ name: "FK___EFIdentityApiKeyClaim___EFIdentityApiKey_ApiKeyId",
+ column: x => x.ApiKeyId,
+ principalTable: "__EFIdentityApiKey",
+ principalColumn: "Id",
+ onDelete: ReferentialAction.Cascade);
+ })
+ .Annotation("MySql:CharSet", "utf8mb4");
+
+ migrationBuilder.CreateTable(
+ name: "__EFIdentityApiKeyRole",
+ columns: table => new
+ {
+ Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
+ ApiKeyId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
+ RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci")
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK___EFIdentityApiKeyRole", x => x.Id);
+ table.ForeignKey(
+ name: "FK___EFIdentityApiKeyRole___EFIdentityApiKey_ApiKeyId",
+ column: x => x.ApiKeyId,
+ principalTable: "__EFIdentityApiKey",
+ principalColumn: "Id",
+ onDelete: ReferentialAction.Cascade);
+ table.ForeignKey(
+ name: "FK___EFIdentityApiKeyRole___EFIdentityRole_RoleId",
+ column: x => x.RoleId,
+ principalTable: "__EFIdentityRole",
+ principalColumn: "Id",
+ onDelete: ReferentialAction.Cascade);
+ })
+ .Annotation("MySql:CharSet", "utf8mb4");
+
+ migrationBuilder.CreateIndex(
+ name: "IX___EFAudit_CreatedBy",
+ table: "__EFAudit",
+ column: "CreatedBy");
+
+ migrationBuilder.CreateIndex(
+ name: "IX___EFAudit_EntityKey",
+ table: "__EFAudit",
+ column: "EntityKey");
+
+ migrationBuilder.CreateIndex(
+ name: "IX___EFAudit_EntityState",
+ table: "__EFAudit",
+ column: "EntityState");
+
+ migrationBuilder.CreateIndex(
+ name: "IX___EFAudit_EntityTypeName",
+ table: "__EFAudit",
+ column: "EntityTypeName");
+
+ migrationBuilder.CreateIndex(
+ name: "IX___EFAudit_RequestId",
+ table: "__EFAudit",
+ column: "RequestId");
+
+ migrationBuilder.CreateIndex(
+ name: "IX___EFAuditProperties_ParentId",
+ table: "__EFAuditProperties",
+ column: "ParentId");
+
+ migrationBuilder.CreateIndex(
+ name: "IX___EFAuditProperties_PropertyName",
+ table: "__EFAuditProperties",
+ column: "PropertyName");
+
+ migrationBuilder.CreateIndex(
+ name: "IX___EFIdentityApiKey_IdentityUserId",
+ table: "__EFIdentityApiKey",
+ column: "IdentityUserId");
+
+ migrationBuilder.CreateIndex(
+ name: "IX___EFIdentityApiKey_RevokedAt",
+ table: "__EFIdentityApiKey",
+ column: "RevokedAt");
+
+ migrationBuilder.CreateIndex(
+ name: "UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType",
+ table: "__EFIdentityApiKeyClaim",
+ columns: new[] { "ApiKeyId", "ClaimType" },
+ unique: true);
+
+ migrationBuilder.CreateIndex(
+ name: "IX___EFIdentityApiKeyRole_RoleId",
+ table: "__EFIdentityApiKeyRole",
+ column: "RoleId");
+
+ migrationBuilder.CreateIndex(
+ name: "UX___EFIdentityApiKeyRole_ApiKeyId_RoleId",
+ table: "__EFIdentityApiKeyRole",
+ columns: new[] { "ApiKeyId", "RoleId" },
+ unique: true);
+
+ migrationBuilder.CreateIndex(
+ name: "RoleNameIndex",
+ table: "__EFIdentityRole",
+ column: "NormalizedName",
+ unique: true);
+
+ migrationBuilder.CreateIndex(
+ name: "IX___EFIdentityRoleClaim_RoleId",
+ table: "__EFIdentityRoleClaim",
+ column: "RoleId");
+
+ migrationBuilder.CreateIndex(
+ name: "EmailIndex",
+ table: "__EFIdentityUser",
+ column: "NormalizedEmail");
+
+ migrationBuilder.CreateIndex(
+ name: "IX___EFIdentityUser_Email",
+ table: "__EFIdentityUser",
+ column: "Email",
+ unique: true);
+
+ migrationBuilder.CreateIndex(
+ name: "IX___EFIdentityUser_IsActive",
+ table: "__EFIdentityUser",
+ column: "IsActive");
+
+ migrationBuilder.CreateIndex(
+ name: "IX___EFIdentityUser_PhoneNumber",
+ table: "__EFIdentityUser",
+ column: "PhoneNumber",
+ unique: true);
+
+ migrationBuilder.CreateIndex(
+ name: "UserNameIndex",
+ table: "__EFIdentityUser",
+ column: "NormalizedUserName",
+ unique: true);
+
+ migrationBuilder.CreateIndex(
+ name: "UX___EFIdentityUserChangeData_IdentityUserId",
+ table: "__EFIdentityUserChangeData",
+ column: "IdentityUserId",
+ unique: true);
+
+ migrationBuilder.CreateIndex(
+ name: "IX___EFIdentityUserClaim_UserId",
+ table: "__EFIdentityUserClaim",
+ column: "UserId");
+
+ migrationBuilder.CreateIndex(
+ name: "IX___EFIdentityUserLogin_UserId",
+ table: "__EFIdentityUserLogin",
+ column: "UserId");
+
+ migrationBuilder.CreateIndex(
+ name: "IX___EFIdentityUserRefreshToken_ExpireAt",
+ table: "__EFIdentityUserRefreshToken",
+ column: "ExpireAt");
+
+ migrationBuilder.CreateIndex(
+ name: "UX___EFIdentityUserRefreshToken_IdentityUserId_AppId",
+ table: "__EFIdentityUserRefreshToken",
+ columns: new[] { "IdentityUserId", "AppId" },
+ unique: true);
+
+ migrationBuilder.CreateIndex(
+ name: "IX___EFIdentityUserRole_RoleId",
+ table: "__EFIdentityUserRole",
+ column: "RoleId");
+
+ migrationBuilder.CreateIndex(
+ name: "IX_Example_CreatedAt",
+ table: "Example",
+ column: "CreatedAt");
+
+ migrationBuilder.CreateIndex(
+ name: "IX_Example_IsDeleted",
+ table: "Example",
+ column: "IsDeleted");
+
+ migrationBuilder.CreateIndex(
+ name: "IX_Example_NavigationId",
+ table: "Example",
+ column: "NavigationId");
+
+ migrationBuilder.CreateIndex(
+ name: "IX_ExampleNavigation_CreatedAt",
+ table: "ExampleNavigation",
+ column: "CreatedAt");
+
+ migrationBuilder.CreateIndex(
+ name: "IX_ExampleNavigation_IsDeleted",
+ table: "ExampleNavigation",
+ column: "IsDeleted");
+
+ migrationBuilder.CreateIndex(
+ name: "IX_ExampleNoAudit_CreatedAt",
+ table: "ExampleNoAudit",
+ column: "CreatedAt");
+
+ migrationBuilder.CreateIndex(
+ name: "IX_ExampleNoAudit_IsDeleted",
+ table: "ExampleNoAudit",
+ column: "IsDeleted");
+ }
+
+ ///
+ protected override void Down(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.DropTable(
+ name: "__EFAuditProperties");
+
+ migrationBuilder.DropTable(
+ name: "__EFDataProtectionKeys");
+
+ migrationBuilder.DropTable(
+ name: "__EFIdentityApiKeyClaim");
+
+ migrationBuilder.DropTable(
+ name: "__EFIdentityApiKeyRole");
+
+ migrationBuilder.DropTable(
+ name: "__EFIdentityRoleClaim");
+
+ migrationBuilder.DropTable(
+ name: "__EFIdentityUserChangeData");
+
+ migrationBuilder.DropTable(
+ name: "__EFIdentityUserClaim");
+
+ migrationBuilder.DropTable(
+ name: "__EFIdentityUserLogin");
+
+ migrationBuilder.DropTable(
+ name: "__EFIdentityUserRefreshToken");
+
+ migrationBuilder.DropTable(
+ name: "__EFIdentityUserRole");
+
+ migrationBuilder.DropTable(
+ name: "__EFIdentityUserToken");
+
+ migrationBuilder.DropTable(
+ name: "Example");
+
+ migrationBuilder.DropTable(
+ name: "ExampleNoAudit");
+
+ migrationBuilder.DropTable(
+ name: "__EFAudit");
+
+ migrationBuilder.DropTable(
+ name: "__EFIdentityApiKey");
+
+ migrationBuilder.DropTable(
+ name: "__EFIdentityRole");
+
+ migrationBuilder.DropTable(
+ name: "ExampleNavigation");
+
+ migrationBuilder.DropTable(
+ name: "__EFIdentityUser");
+ }
+ }
+}
diff --git a/Api.ApiClients.Audit/Api.ApiClients.Audit.Service/Migrations/MySqlDbContextModelSnapshot.cs b/Api.ApiClients.Audit/Api.ApiClients.Audit.Service/Migrations/MySqlDbContextModelSnapshot.cs
new file mode 100644
index 00000000..ee5255c2
--- /dev/null
+++ b/Api.ApiClients.Audit/Api.ApiClients.Audit.Service/Migrations/MySqlDbContextModelSnapshot.cs
@@ -0,0 +1,725 @@
+//
+using System;
+using Api.ApiClients.Audit.Service.Data;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Metadata;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+
+#nullable disable
+
+namespace Api.Data.Audit.Migrations
+{
+ [DbContext(typeof(MySqlDbContext))]
+ partial class MySqlDbContextModelSnapshot : ModelSnapshot
+ {
+ protected override void BuildModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder
+ .HasAnnotation("ProductVersion", "9.0.11")
+ .HasAnnotation("Relational:MaxIdentifierLength", 64);
+
+ MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder);
+
+ modelBuilder.Entity("Api.Data.Audit.Models.Example", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("char(36)");
+
+ b.Property("CreatedAt")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("datetime(6)");
+
+ MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt"));
+
+ b.Property("IsDeleted")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("bigint")
+ .HasDefaultValue(0L);
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.Property("NavigationId")
+ .HasColumnType("char(36)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("CreatedAt");
+
+ b.HasIndex("IsDeleted");
+
+ b.HasIndex("NavigationId");
+
+ b.ToTable("Example");
+ });
+
+ modelBuilder.Entity("Api.Data.Audit.Models.ExampleNavigation", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("char(36)");
+
+ b.Property("CreatedAt")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("datetime(6)");
+
+ MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt"));
+
+ b.Property("IsDeleted")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("bigint")
+ .HasDefaultValue(0L);
+
+ b.Property("NavigationName")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.HasKey("Id");
+
+ b.HasIndex("CreatedAt");
+
+ b.HasIndex("IsDeleted");
+
+ b.ToTable("ExampleNavigation");
+ });
+
+ modelBuilder.Entity("Api.Data.Audit.Models.ExampleNoAudit", b =>
+ {
+ b.Property