Browse Source

Merge branch 'rel-10.1' into feat/#23891

pull/24547/head
erdemcaygor 7 months ago
parent
commit
2db1b40b9c
  1. 20
      .github/workflows/auto-pr.yml
  2. 15
      Directory.Packages.props
  3. 2
      abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json
  4. 2
      abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/en.json
  5. 173
      docs/en/Blog-Posts/2025-12-24-Announcing-Aspire-For-Microservice-Template/POST.md
  6. BIN
      docs/en/Blog-Posts/2025-12-24-Announcing-Aspire-For-Microservice-Template/aspire-apphost-topology.png
  7. BIN
      docs/en/Blog-Posts/2025-12-24-Announcing-Aspire-For-Microservice-Template/aspire-configuration.png
  8. BIN
      docs/en/Blog-Posts/2025-12-24-Announcing-Aspire-For-Microservice-Template/aspire-dashboard-console.png
  9. BIN
      docs/en/Blog-Posts/2025-12-24-Announcing-Aspire-For-Microservice-Template/aspire-dashboard-metrics.png
  10. BIN
      docs/en/Blog-Posts/2025-12-24-Announcing-Aspire-For-Microservice-Template/aspire-dashboard-resources.png
  11. BIN
      docs/en/Blog-Posts/2025-12-24-Announcing-Aspire-For-Microservice-Template/aspire-dashboard-structured-logs.png
  12. BIN
      docs/en/Blog-Posts/2025-12-24-Announcing-Aspire-For-Microservice-Template/aspire-dashboard-traces.png
  13. BIN
      docs/en/Blog-Posts/2025-12-24-Announcing-Aspire-For-Microservice-Template/aspire-database-postgre-pgadmin.png
  14. BIN
      docs/en/Blog-Posts/2025-12-24-Announcing-Aspire-For-Microservice-Template/aspire-grafana-dashboard.png
  15. BIN
      docs/en/Blog-Posts/2025-12-24-Announcing-Aspire-For-Microservice-Template/aspire-jaeger-traces.png
  16. BIN
      docs/en/Blog-Posts/2025-12-24-Announcing-Aspire-For-Microservice-Template/aspire-kibana-dashboard.png
  17. BIN
      docs/en/Blog-Posts/2025-12-24-Announcing-Aspire-For-Microservice-Template/aspire-prometheus-dashboard.png
  18. BIN
      docs/en/Blog-Posts/2025-12-24-Announcing-Aspire-For-Microservice-Template/aspire-rabbitmq-management.png
  19. BIN
      docs/en/Blog-Posts/2025-12-24-Announcing-Aspire-For-Microservice-Template/aspire-redis-insight.png
  20. BIN
      docs/en/Blog-Posts/2025-12-24-Announcing-Aspire-For-Microservice-Template/aspire-solution-structure.png
  21. BIN
      docs/en/Blog-Posts/2025-12-24-Announcing-Aspire-For-Microservice-Template/solution-runner-aspire-profile.png
  22. BIN
      docs/en/Community-Articles/2025-12-13-Building-Dynamic-XML-Sitemaps-With-ABP-Framework/cover.png
  23. 122
      docs/en/Community-Articles/2025-12-13-Building-Dynamic-XML-Sitemaps-With-ABP-Framework/images/sitemap-architecture.svg
  24. 475
      docs/en/Community-Articles/2025-12-13-Building-Dynamic-XML-Sitemaps-With-ABP-Framework/post.md
  25. 1
      docs/en/Community-Articles/2025-12-13-Building-Dynamic-XML-Sitemaps-With-ABP-Framework/summary.md
  26. BIN
      docs/en/Community-Articles/2025-12-18-Announcement-AIMAnagement/cover.png
  27. BIN
      docs/en/Community-Articles/2025-12-18-Announcement-AIMAnagement/images/abp-studio-ai-management.png
  28. BIN
      docs/en/Community-Articles/2025-12-18-Announcement-AIMAnagement/images/ai-management-workspace-playground.png
  29. BIN
      docs/en/Community-Articles/2025-12-18-Announcement-AIMAnagement/images/ai-management-workspace-widget.png
  30. BIN
      docs/en/Community-Articles/2025-12-18-Announcement-AIMAnagement/images/aimanagement-workspace-geminiasopenai.png
  31. 106
      docs/en/Community-Articles/2025-12-18-Announcement-AIMAnagement/post.md
  32. 38
      docs/en/Community-Articles/2025-12-23-Referral-Program-Announcement/post.md
  33. 26
      docs/en/docs-nav.json
  34. 4
      docs/en/framework/architecture/multi-tenancy/index.md
  35. 2
      docs/en/framework/fundamentals/dependency-injection.md
  36. 4
      docs/en/framework/fundamentals/exception-handling.md
  37. 2
      docs/en/framework/fundamentals/validation.md
  38. 4
      docs/en/framework/infrastructure/artificial-intelligence/index.md
  39. 218
      docs/en/framework/infrastructure/artificial-intelligence/microsoft-agent-framework.md
  40. 1
      docs/en/framework/infrastructure/artificial-intelligence/microsoft-extensions-ai.md
  41. 2
      docs/en/framework/infrastructure/audit-logging.md
  42. 2
      docs/en/framework/infrastructure/background-workers/hangfire.md
  43. 2
      docs/en/framework/infrastructure/background-workers/quartz.md
  44. 2
      docs/en/framework/infrastructure/current-user.md
  45. 4
      docs/en/framework/infrastructure/emailing.md
  46. 23
      docs/en/framework/infrastructure/features.md
  47. 2
      docs/en/framework/infrastructure/settings.md
  48. 262
      docs/en/framework/ui/angular/ai-config.md
  49. 384
      docs/en/framework/ui/angular/ssr-configuration.md
  50. 126
      docs/en/framework/ui/common/leptonx-css-variables.md
  51. BIN
      docs/en/get-started/images/abp-studio-new-microservice-solution-dialog-optional-modules.png
  52. BIN
      docs/en/get-started/images/abp-studio-new-microservice-solution-dialog-properties.png
  53. BIN
      docs/en/get-started/images/abp-studio-new-solution-dialog-additional-options-microservice.png
  54. BIN
      docs/en/get-started/images/abp-studio-new-solution-dialog-additional-services.png
  55. BIN
      docs/en/get-started/images/abp-studio-new-solution-dialog-aspire-configuration-microservice.png
  56. BIN
      docs/en/get-started/images/abp-studio-new-solution-dialog-database-configurations-microservice.png
  57. BIN
      docs/en/get-started/images/abp-studio-new-solution-dialog-database-provider-microservice.png
  58. BIN
      docs/en/get-started/images/abp-studio-new-solution-dialog-dynamic-localization.png
  59. BIN
      docs/en/get-started/images/abp-studio-new-solution-dialog-languages-microservice.png
  60. BIN
      docs/en/get-started/images/abp-studio-new-solution-dialog-microservice.png
  61. BIN
      docs/en/get-started/images/abp-studio-new-solution-dialog-mobile-framework-microservice.png
  62. BIN
      docs/en/get-started/images/abp-studio-new-solution-dialog-multi-tenancy.png
  63. BIN
      docs/en/get-started/images/abp-studio-new-solution-dialog-public-web-site.png
  64. BIN
      docs/en/get-started/images/abp-studio-new-solution-dialog-ui-framework-microservice.png
  65. BIN
      docs/en/get-started/images/abp-studio-new-solution-dialog-ui-theme-microservice.png
  66. 19
      docs/en/get-started/microservice.md
  67. BIN
      docs/en/images/add-passkey.png
  68. BIN
      docs/en/images/ai-management-widget.png
  69. BIN
      docs/en/images/my-passkey.png
  70. BIN
      docs/en/images/passkey-login.png
  71. BIN
      docs/en/images/passkey-login2.png
  72. BIN
      docs/en/images/passkey-setting.png
  73. BIN
      docs/en/images/pen-test-alert-list-10.1.png
  74. 1
      docs/en/modules/account-pro.md
  75. 2
      docs/en/modules/account/impersonation.md
  76. 63
      docs/en/modules/account/passkey.md
  77. 253
      docs/en/modules/ai-management/index.md
  78. 3
      docs/en/modules/feature-management.md
  79. 2
      docs/en/modules/saas.md
  80. 88
      docs/en/others/penetration-test-report.md
  81. 372
      docs/en/solution-templates/microservice/aspire-integration.md
  82. 4
      docs/en/solution-templates/microservice/helm-charts-and-kubernetes.md
  83. BIN
      docs/en/solution-templates/microservice/images/aspire-apphost-topology.png
  84. BIN
      docs/en/solution-templates/microservice/images/aspire-configuration.png
  85. BIN
      docs/en/solution-templates/microservice/images/aspire-dashboard-console.png
  86. BIN
      docs/en/solution-templates/microservice/images/aspire-dashboard-login.png
  87. BIN
      docs/en/solution-templates/microservice/images/aspire-dashboard-metrics.png
  88. BIN
      docs/en/solution-templates/microservice/images/aspire-dashboard-resources.png
  89. BIN
      docs/en/solution-templates/microservice/images/aspire-dashboard-structured-logs.png
  90. BIN
      docs/en/solution-templates/microservice/images/aspire-dashboard-traces.png
  91. BIN
      docs/en/solution-templates/microservice/images/aspire-database-postgre-pgadmin.png
  92. BIN
      docs/en/solution-templates/microservice/images/aspire-grafana-dashboard.png
  93. BIN
      docs/en/solution-templates/microservice/images/aspire-jaeger-traces.png
  94. BIN
      docs/en/solution-templates/microservice/images/aspire-kibana-dashboard.png
  95. BIN
      docs/en/solution-templates/microservice/images/aspire-prometheus-dashboard.png
  96. BIN
      docs/en/solution-templates/microservice/images/aspire-rabbitmq-management.png
  97. BIN
      docs/en/solution-templates/microservice/images/aspire-redis-insight.png
  98. BIN
      docs/en/solution-templates/microservice/images/aspire-solution-structure.png
  99. BIN
      docs/en/solution-templates/microservice/images/solution-runner-aspire-profile.png
  100. BIN
      docs/en/solution-templates/microservice/images/solution-runner-default-profile.png

20
.github/workflows/auto-pr.yml

@ -1,13 +1,13 @@
name: Merge branch dev with rel-10.0
name: Merge branch dev with rel-10.1
on:
push:
branches:
- rel-10.0
- rel-10.1
permissions:
contents: read
jobs:
merge-dev-with-rel-10-0:
merge-dev-with-rel-10-1:
permissions:
contents: write # for peter-evans/create-pull-request to create branch
pull-requests: write # for peter-evans/create-pull-request to create a PR
@ -18,14 +18,14 @@ jobs:
ref: dev
- name: Reset promotion branch
run: |
git fetch origin rel-10.0:rel-10.0
git reset --hard rel-10.0
git fetch origin rel-10.1:rel-10.1
git reset --hard rel-10.1
- name: Create Pull Request
uses: peter-evans/create-pull-request@v3
with:
branch: auto-merge/rel-10-0/${{github.run_number}}
title: Merge branch dev with rel-10.0
body: This PR generated automatically to merge dev with rel-10.0. Please review the changed files before merging to prevent any errors that may occur.
branch: auto-merge/rel-10-1/${{github.run_number}}
title: Merge branch dev with rel-10.1
body: This PR generated automatically to merge dev with rel-10.1. Please review the changed files before merging to prevent any errors that may occur.
reviewers: maliming
draft: true
token: ${{ github.token }}
@ -34,5 +34,5 @@ jobs:
GH_TOKEN: ${{ secrets.BOT_SECRET }}
run: |
gh pr ready
gh pr review auto-merge/rel-10-0/${{github.run_number}} --approve
gh pr merge auto-merge/rel-10-0/${{github.run_number}} --merge --auto --delete-branch
gh pr review auto-merge/rel-10-1/${{github.run_number}} --approve
gh pr merge auto-merge/rel-10-1/${{github.run_number}} --merge --auto --delete-branch

15
Directory.Packages.props

@ -7,7 +7,6 @@
<PackageVersion Include="AlibabaCloud.SDK.Dysmsapi20170525" Version="4.0.0" />
<PackageVersion Include="aliyun-net-sdk-sts" Version="3.1.3" />
<PackageVersion Include="Aliyun.OSS.SDK.NetCore" Version="2.14.1" />
<PackageVersion Include="AsyncKeyedLock" Version="7.1.8" />
<PackageVersion Include="Autofac" Version="8.4.0" />
<PackageVersion Include="Autofac.Extensions.DependencyInjection" Version="10.0.0" />
<PackageVersion Include="Autofac.Extras.DynamicProxy" Version="7.1.0" />
@ -20,10 +19,10 @@
<PackageVersion Include="Azure.Identity" Version="1.14.2" />
<PackageVersion Include="Azure.Messaging.ServiceBus" Version="7.20.1" />
<PackageVersion Include="Azure.Storage.Blobs" Version="12.25.0" />
<PackageVersion Include="Blazorise" Version="1.8.6" />
<PackageVersion Include="Blazorise.Components" Version="1.8.6" />
<PackageVersion Include="Blazorise.DataGrid" Version="1.8.6" />
<PackageVersion Include="Blazorise.Snackbar" Version="1.8.6" />
<PackageVersion Include="Blazorise" Version="1.8.8" />
<PackageVersion Include="Blazorise.Components" Version="1.8.8" />
<PackageVersion Include="Blazorise.DataGrid" Version="1.8.8" />
<PackageVersion Include="Blazorise.Snackbar" Version="1.8.8" />
<PackageVersion Include="Castle.Core" Version="5.2.1" />
<PackageVersion Include="Castle.Core.AsyncInterceptor" Version="2.1.0" />
<PackageVersion Include="CommonMark.NET" Version="0.15.1" />
@ -137,11 +136,11 @@
<PackageVersion Include="OpenIddict.Server.AspNetCore" Version="7.2.0" />
<PackageVersion Include="OpenIddict.Validation.AspNetCore" Version="7.2.0" />
<PackageVersion Include="OpenIddict.Validation.ServerIntegration" Version="7.2.0" />
<PackageVersion Include="Oracle.EntityFrameworkCore" Version="9.23.90" />
<PackageVersion Include="Oracle.EntityFrameworkCore" Version="10.23.26000" />
<PackageVersion Include="Polly" Version="8.6.3" />
<PackageVersion Include="Polly.Extensions.Http" Version="3.0.0" />
<PackageVersion Include="Pomelo.EntityFrameworkCore.MySql" Version="9.0.0" />
<PackageVersion Include="MySql.EntityFrameworkCore" Version="10.0.0-preview" />
<PackageVersion Include="MySql.EntityFrameworkCore" Version="10.0.0-rc" />
<PackageVersion Include="Quartz" Version="3.15.0" />
<PackageVersion Include="Quartz.Extensions.DependencyInjection" Version="3.15.0" />
<PackageVersion Include="Quartz.Plugins.TimeZoneConverter" Version="3.15.0" />
@ -149,7 +148,7 @@
<PackageVersion Include="RabbitMQ.Client" Version="7.1.2" />
<PackageVersion Include="Rebus" Version="8.8.0" />
<PackageVersion Include="Rebus.ServiceProvider" Version="10.5.0" />
<PackageVersion Include="Riok.Mapperly" Version="4.3.0" />
<PackageVersion Include="Riok.Mapperly" Version="4.3.1" />
<PackageVersion Include="Scriban" Version="6.3.0" />
<PackageVersion Include="Serilog" Version="4.3.0" />
<PackageVersion Include="Serilog.AspNetCore" Version="9.0.0" />

2
abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json

@ -783,7 +783,7 @@
"Enum:SourceChannel:1": "Studio",
"Enum:SourceChannel:2": "Support Site",
"Enum:SourceChannel:3": "Suite",
"Menu:OrganizationTokenUsage": "Organization Token Usage",
"Menu:AITokens": "AI Tokens",
"Permission:OrganizationTokenUsage": "Organization Token Usage"
}
}

2
abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/en.json

@ -269,7 +269,7 @@
"Referral.CannotDeleteUsedLink": "You cannot delete a referral link that has already been used.",
"Referral.CannotReferYourself": "You cannot create a referral link for your own email address.",
"Referral:TargetEmail": "Target Email",
"Referral.CannotReferSameOrganizationMember": "You cannot create a referral link for a user who is already a member of your organization.",
"Referral.CannotReferSameOrganizationMember": "Referral links cannot be used for existing organization members.",
"LinkCopiedToClipboard": "Link copied to clipboard",
"AreYouSureToDeleteReferralLink": "Are you sure you want to delete this referral link?",
"DefaultErrorMessage": "An error occurred."

173
docs/en/Blog-Posts/2025-12-24-Announcing-Aspire-For-Microservice-Template/POST.md

@ -0,0 +1,173 @@
# Announcing .NET Aspire Integration for ABP Microservice Template
We are excited to announce the integration of **.NET Aspire** into the ABP microservice solution, available starting with **ABP Studio v2.0.0**. This integration brings a unified development experience for building, running, debugging, and deploying distributed applications. With Aspire, you can now orchestrate your entire microservice ecosystem with a single command, eliminating complex configurations and making local development effortless.
## What is .NET Aspire?
[Aspire](https://aspire.dev/get-started/what-is-aspire/) is a cloud-ready stack designed to streamline the development of distributed applications. It provides:
- **Orchestration**: A code-first approach to defining and running distributed applications, managing dependencies, and launch order.
- **Integrations**: Pre-built components for common services (databases, caches, message brokers) with automatic configuration.
- **Tooling**: A developer dashboard for real-time monitoring of logs, traces, metrics, and resource health.
- **Service Discovery**: Automatic service-to-service communication without hardcoded endpoints.
- **Observability**: Built-in OpenTelemetry support for distributed tracing, metrics, and structured logging.
## How Does It Work with ABP?
When you enable .NET Aspire in an ABP microservice solution, you get a fully integrated development experience where:
- All microservices, gateways, and applications are orchestrated through a single entry point (AppHost).
- Infrastructure containers (databases, Redis, RabbitMQ, Elasticsearch, etc.) are managed as code.
- OpenTelemetry, health checks, and service discovery are automatically configured for all projects via the shared ServiceDefaults project.
## Enabling Aspire in Your Solution
When creating a new microservice solution via ABP Studio:
1. In the solution creation wizard, look for the **".NET Aspire Integration"** step.
2. Toggle the option to **enable .NET Aspire**.
3. Complete the wizard—Aspire projects will be generated along with your solution.
![Enable Aspire in ABP Studio](aspire-configuration.png)
## Solution Structure Changes
When Aspire is enabled, two additional projects are added to your solution:
![Aspire Solution Structure](aspire-solution-structure.png)
### AppHost (Orchestrator)
[`AppHost`](https://aspire.dev/get-started/app-host/) is the .NET Aspire orchestrator project that declares all resources (services, databases, containers, applications) and their dependencies in C# code. It provides:
- **Centralized orchestration**: Start your entire microservice ecosystem with a single command.
- **Code-first infrastructure**: Databases, Redis, RabbitMQ, Elasticsearch, and observability tools are defined programmatically.
- **Dependency management**: Services start in the correct order using `WaitFor()` declarations.
- **Automatic configuration**: Connection strings, endpoints, and environment variables are injected automatically.
### ServiceDefaults
[`ServiceDefaults`](https://aspire.dev/fundamentals/service-defaults/) is a shared library that provides common cloud-native configuration for all projects in the solution. Every service uses the same observability, health check, and resilience patterns.
| Feature | Description |
|---------|-------------|
| OpenTelemetry | Tracing, metrics, and structured logging with automatic instrumentation |
| Health Checks | `/health` and `/alive` endpoints for Kubernetes-style probes |
| Service Discovery | Automatic resolution of service endpoints |
| HTTP Resilience | Retry policies, timeouts, and circuit breakers for HTTP clients |
## Running the Solution with Aspire
Running your microservice solution has never been easier:
1. Open **Solution Runner** in ABP Studio.
2. Select the **Aspire** profile.
3. Run `AppHost`.
![Solution Runner with Aspire](solution-runner-aspire-profile.png)
AppHost automatically:
- Starts all infrastructure containers (database, Redis, RabbitMQ, Elasticsearch, etc.).
- Launches all microservices, gateways, and applications in dependency order.
- Injects connection strings and environment variables.
- Opens the Aspire Dashboard for monitoring.
![Aspire AppHost Resource Topology](aspire-apphost-topology.png)
## Aspire Dashboard
The Aspire Dashboard provides real-time tracking of your application's state. It enables you to monitor logs, traces, metrics, and environment configurations in an intuitive UI.
![Aspire Dashboard Resources](aspire-dashboard-resources.png)
### Key Dashboard Features
#### Console Logs
Display console logs from all resources in real-time. Filter by resource and log level to quickly find relevant information during development and debugging.
![Aspire Dashboard Console](aspire-dashboard-console.png)
#### Structured Logs
View structured logs from all resources with advanced filtering capabilities. Search and filter logs by resource, log level, timestamp, and custom properties.
![Aspire Dashboard Structured Logs](aspire-dashboard-structured-logs.png)
#### Distributed Traces
Explore distributed traces across your microservices to understand request flows and identify performance bottlenecks.
![Aspire Dashboard Traces](aspire-dashboard-traces.png)
#### Metrics
Monitor real-time metrics including HTTP requests, response times, garbage collection, memory usage, and custom metrics.
![Aspire Dashboard Metrics](aspire-dashboard-metrics.png)
## Pre-Configured Observability Tools
AppHost comes with pre-configured observability and management tools:
### Grafana
Visualization and analytics platform for monitoring metrics with interactive dashboards.
![Grafana Dashboard](aspire-grafana-dashboard.png)
### Jaeger
Distributed tracing system to monitor and troubleshoot problems across microservices.
![Jaeger Traces](aspire-jaeger-traces.png)
### Kibana
Visualization tool for Elasticsearch data with search and data visualization capabilities for logs.
![Kibana Dashboard](aspire-kibana-dashboard.png)
### Prometheus
Monitoring and alerting toolkit that collects and stores metrics as time series data.
![Prometheus Dashboard](aspire-prometheus-dashboard.png)
### RabbitMQ Management
Web-based interface for managing and monitoring the RabbitMQ message broker.
![RabbitMQ Management](aspire-rabbitmq-management.png)
### Redis Insight
Visual tool for Redis that allows you to browse data, run commands, and monitor performance.
![Redis Insight](aspire-redis-insight.png)
### Database Admin Tools
The database management admin tool varies by database type:
| Database | Tool |
|----------|------|
| SQL Server | DBeaver CloudBeaver |
| MySQL | phpMyAdmin |
| PostgreSQL | pgAdmin |
| MongoDB | Mongo Express |
![pgAdmin Dashboard](aspire-database-postgre-pgadmin.png)
## Get Started Today
Ready to experience the power of .NET Aspire with ABP? Create a new microservice solution in ABP Studio and enable the .NET Aspire integration option. For detailed documentation, visit our [.NET Aspire Integration documentation](https://abp.io/docs/latest/solution-templates/microservice/aspire-integration).
To learn more about .NET Aspire, visit: [https://aspire.dev](https://aspire.dev/get-started/what-is-aspire/)
We are excited to bring this integration to you and can't wait to hear your feedback. If you have any questions or suggestions, please drop a comment below.
Happy coding!
**The Volosoft Team**

BIN
docs/en/Blog-Posts/2025-12-24-Announcing-Aspire-For-Microservice-Template/aspire-apphost-topology.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

BIN
docs/en/Blog-Posts/2025-12-24-Announcing-Aspire-For-Microservice-Template/aspire-configuration.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

BIN
docs/en/Blog-Posts/2025-12-24-Announcing-Aspire-For-Microservice-Template/aspire-dashboard-console.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 141 KiB

BIN
docs/en/Blog-Posts/2025-12-24-Announcing-Aspire-For-Microservice-Template/aspire-dashboard-metrics.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

BIN
docs/en/Blog-Posts/2025-12-24-Announcing-Aspire-For-Microservice-Template/aspire-dashboard-resources.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

BIN
docs/en/Blog-Posts/2025-12-24-Announcing-Aspire-For-Microservice-Template/aspire-dashboard-structured-logs.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

BIN
docs/en/Blog-Posts/2025-12-24-Announcing-Aspire-For-Microservice-Template/aspire-dashboard-traces.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

BIN
docs/en/Blog-Posts/2025-12-24-Announcing-Aspire-For-Microservice-Template/aspire-database-postgre-pgadmin.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

BIN
docs/en/Blog-Posts/2025-12-24-Announcing-Aspire-For-Microservice-Template/aspire-grafana-dashboard.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

BIN
docs/en/Blog-Posts/2025-12-24-Announcing-Aspire-For-Microservice-Template/aspire-jaeger-traces.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

BIN
docs/en/Blog-Posts/2025-12-24-Announcing-Aspire-For-Microservice-Template/aspire-kibana-dashboard.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 KiB

BIN
docs/en/Blog-Posts/2025-12-24-Announcing-Aspire-For-Microservice-Template/aspire-prometheus-dashboard.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

BIN
docs/en/Blog-Posts/2025-12-24-Announcing-Aspire-For-Microservice-Template/aspire-rabbitmq-management.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

BIN
docs/en/Blog-Posts/2025-12-24-Announcing-Aspire-For-Microservice-Template/aspire-redis-insight.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

BIN
docs/en/Blog-Posts/2025-12-24-Announcing-Aspire-For-Microservice-Template/aspire-solution-structure.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

BIN
docs/en/Blog-Posts/2025-12-24-Announcing-Aspire-For-Microservice-Template/solution-runner-aspire-profile.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

BIN
docs/en/Community-Articles/2025-12-13-Building-Dynamic-XML-Sitemaps-With-ABP-Framework/cover.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

122
docs/en/Community-Articles/2025-12-13-Building-Dynamic-XML-Sitemaps-With-ABP-Framework/images/sitemap-architecture.svg

@ -0,0 +1,122 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1000 700">
<defs>
<style>
.box { fill: #ffffff; stroke: #1890ff; stroke-width: 2; }
.title-box { fill: #1890ff; stroke: #1890ff; stroke-width: 2; }
.text { font-family: Arial, sans-serif; font-size: 14px; fill: #333333; font-weight: 600; }
.title-text { font-family: Arial, sans-serif; font-size: 14px; fill: #ffffff; font-weight: 600; }
.small-text { font-family: Arial, sans-serif; font-size: 11px; fill: #666666; }
.arrow { stroke: #1890ff; stroke-width: 2; fill: none; marker-end: url(#arrowhead); }
.layer-label { font-family: Arial, sans-serif; font-size: 16px; fill: #1890ff; font-weight: bold; }
</style>
<marker id="arrowhead" markerWidth="10" markerHeight="10" refX="9" refY="3" orient="auto">
<polygon points="0 0, 10 3, 0 6" fill="#1890ff" />
</marker>
</defs>
<!-- Title -->
<text x="500" y="30" text-anchor="middle" class="layer-label" style="font-size: 20px;">Sitemap Module Architecture</text>
<!-- Discovery Layer -->
<text x="50" y="80" class="layer-label">1. Discovery Layer</text>
<rect class="title-box" x="50" y="90" width="220" height="35" rx="5" />
<text class="title-text" x="160" y="112" text-anchor="middle">RazorPageDiscoveryService</text>
<rect class="box" x="50" y="125" width="220" height="60" rx="5" />
<text class="small-text" x="160" y="145" text-anchor="middle">• Scans assemblies</text>
<text class="small-text" x="160" y="163" text-anchor="middle">• Finds PageModel classes</text>
<text class="small-text" x="160" y="181" text-anchor="middle">• Extracts route metadata</text>
<!-- Arrow down -->
<path class="arrow" d="M 160 185 L 160 225" />
<!-- Source Layer -->
<text x="50" y="245" class="layer-label">2. Source Layer</text>
<!-- Static Source -->
<rect class="title-box" x="50" y="260" width="200" height="35" rx="5" />
<text class="title-text" x="150" y="282" text-anchor="middle">IStaticPageSitemapSource</text>
<rect class="box" x="50" y="295" width="200" height="80" rx="5" />
<text class="small-text" x="150" y="315" text-anchor="middle">Processes attributes:</text>
<text class="small-text" x="150" y="333" text-anchor="middle">[IncludeSitemapXml]</text>
<text class="small-text" x="150" y="351" text-anchor="middle">Returns static page</text>
<text class="small-text" x="150" y="369" text-anchor="middle">sitemap items</text>
<!-- Dynamic Source -->
<rect class="title-box" x="280" y="260" width="200" height="35" rx="5" />
<text class="title-text" x="380" y="282" text-anchor="middle">IGroupedSitemapSource</text>
<rect class="box" x="280" y="295" width="200" height="80" rx="5" />
<text class="small-text" x="380" y="315" text-anchor="middle">Queries repositories:</text>
<text class="small-text" x="380" y="333" text-anchor="middle">Books, Articles, Products</text>
<text class="small-text" x="380" y="351" text-anchor="middle">Returns dynamic</text>
<text class="small-text" x="380" y="369" text-anchor="middle">sitemap items</text>
<!-- Custom Source -->
<rect class="title-box" x="510" y="260" width="200" height="35" rx="5" />
<text class="title-text" x="610" y="282" text-anchor="middle">Custom Sources</text>
<rect class="box" x="510" y="295" width="200" height="80" rx="5" />
<text class="small-text" x="610" y="315" text-anchor="middle">Implement</text>
<text class="small-text" x="610" y="333" text-anchor="middle">ISitemapItemSource</text>
<text class="small-text" x="610" y="351" text-anchor="middle">for custom logic</text>
<!-- Arrows to Collection Layer -->
<path class="arrow" d="M 150 375 L 150 420 L 400 420 L 400 445" />
<path class="arrow" d="M 380 375 L 380 420 L 400 420 L 400 445" />
<path class="arrow" d="M 610 375 L 610 420 L 400 420 L 400 445" />
<!-- Collection Layer -->
<text x="50" y="465" class="layer-label">3. Collection Layer</text>
<rect class="title-box" x="250" y="480" width="300" height="35" rx="5" />
<text class="title-text" x="400" y="502" text-anchor="middle">SitemapItemCollector</text>
<rect class="box" x="250" y="515" width="300" height="80" rx="5" />
<text class="small-text" x="400" y="535" text-anchor="middle">• Aggregates items from all sources</text>
<text class="small-text" x="400" y="553" text-anchor="middle">• Groups by category (Main, Blog, Products)</text>
<text class="small-text" x="400" y="571" text-anchor="middle">• Removes duplicates</text>
<text class="small-text" x="400" y="589" text-anchor="middle">• Returns Dictionary&lt;Group, Items&gt;</text>
<!-- Arrow down -->
<path class="arrow" d="M 400 595 L 400 635" />
<!-- Generation Layer -->
<text x="650" y="465" class="layer-label">4. Generation Layer</text>
<rect class="title-box" x="640" y="480" width="280" height="35" rx="5" />
<text class="title-text" x="780" y="502" text-anchor="middle">SitemapXmlGenerator</text>
<rect class="box" x="640" y="515" width="280" height="80" rx="5" />
<text class="small-text" x="780" y="535" text-anchor="middle">• Converts items to XML format</text>
<text class="small-text" x="780" y="553" text-anchor="middle">• Adds &lt;loc&gt;, &lt;lastmod&gt;, &lt;priority&gt;</text>
<text class="small-text" x="780" y="571" text-anchor="middle">• Validates against sitemap protocol</text>
<text class="small-text" x="780" y="589" text-anchor="middle">• Returns XML string</text>
<!-- Arrow from Collection to Generation -->
<path class="arrow" d="M 550 555 L 640 555" />
<!-- Management Layer -->
<text x="50" y="655" class="layer-label">5. Management Layer</text>
<!-- File Generator -->
<rect class="title-box" x="50" y="470" width="180" height="35" rx="5" />
<text class="title-text" x="140" y="492" text-anchor="middle">SitemapFileGenerator</text>
<rect class="box" x="50" y="505" width="180" height="90" rx="5" />
<text class="small-text" x="140" y="525" text-anchor="middle">• Orchestrates collection</text>
<text class="small-text" x="140" y="543" text-anchor="middle">• Calls XML generator</text>
<text class="small-text" x="140" y="561" text-anchor="middle">• Writes files to disk:</text>
<text class="small-text" x="140" y="579" text-anchor="middle">Sitemaps/</text>
<!-- Background Worker -->
<rect class="title-box" x="250" y="640" width="300" height="35" rx="5" />
<text class="title-text" x="400" y="662" text-anchor="middle">SitemapRegenerationWorker</text>
<rect class="box" x="250" y="675" width="300" height="60" rx="5" />
<text class="small-text" x="400" y="695" text-anchor="middle">• Runs periodically (e.g., hourly)</text>
<text class="small-text" x="400" y="713" text-anchor="middle">• Triggers SitemapFileGenerator</text>
<text class="small-text" x="400" y="731" text-anchor="middle">• Non-blocking background execution</text>
<!-- Arrow from Worker to File Generator -->
<path class="arrow" d="M 250 705 L 230 550" />
<!-- Output Files -->
<text x="750" y="625" class="small-text" fill="#666666">📄 sitemap.xml</text>
<text x="750" y="643" class="small-text" fill="#666666">📄 sitemap-Blog.xml</text>
<text x="750" y="661" class="small-text" fill="#666666">📄 sitemap-Products.xml</text>
<!-- Arrow from Generation to Files -->
<path class="arrow" d="M 780 595 L 780 620" />
</svg>

After

Width:  |  Height:  |  Size: 7.2 KiB

475
docs/en/Community-Articles/2025-12-13-Building-Dynamic-XML-Sitemaps-With-ABP-Framework/post.md

@ -0,0 +1,475 @@
# Building Dynamic XML Sitemaps with ABP Framework
Search Engine Optimization (SEO) is crucial for any web application that wants to be discovered by users. One of the most fundamental SEO practices is providing a comprehensive XML sitemap that helps search engines crawl and index your website efficiently. In this article, we'll use a reusable ABP module that automatically generates dynamic XML sitemaps for both static Razor Pages and dynamic content from your database.
By the end of this tutorial, you'll have a production-ready sitemap solution that discovers your pages automatically, includes dynamic content like blog posts or products, and regenerates sitemaps in the background without impacting performance.
## What is an XML Sitemap?
An XML sitemap is a file that lists all important pages of your website in a structured format that search engines can easily read. It acts as a roadmap for crawlers like Google, Bing, and others, telling them which pages exist, when they were last updated, and how they relate to each other.
For modern web applications with dynamic content, manually maintaining sitemap files quickly becomes impractical. A dynamic sitemap solution that automatically discovers and updates URLs is essential for:
- **Large content sites** with frequently changing blog posts, articles, or products
- **Multi-tenant applications** where each tenant may have different content
- **Enterprise applications** with complex page hierarchies
- **E-commerce platforms** with thousands of product pages
## Why Build a Custom Sitemap Module?
While there are general-purpose sitemap libraries available, building a custom module for ABP Framework provides several advantages:
**Deep ABP Integration**: Leverages ABP's dependency injection, background workers, and module system
**Automatic Discovery**: Uses ASP.NET Core's Razor Page infrastructure to automatically find pages
**Type-Safe Configuration**: Strongly-typed attributes and options for configuration
**Multi-Group Support**: Organize sitemaps by logical groups (main, blog, products, etc.)
**Background Generation**: Non-blocking sitemap regeneration using ABP's background worker system
**Repository Integration**: Direct integration with ABP repositories for database entities
## Project Architecture Overview
Before using the module, let's understand its architecture:
![Architecture Diagram](./images/sitemap-architecture.svg)
The sitemap module consists of several key components:
1. **Discovery Layer**: Discovers Razor Pages and their metadata using reflection
2. **Source Layer**: Defines contracts for providing sitemap items (static pages and dynamic content)
3. **Collection Layer**: Collects items from all registered sources
4. **Generation Layer**: Transforms collected items into XML format
5. **Management Layer**: Orchestrates file generation and background workers
## Installation
To get started, clone the demo repository which includes the sitemap module:
```bash
git clone https://github.com/salihozkara/AbpSitemapDemo
cd AbpSitemapDemo
```
The repository contains the sitemap module in the `Modules/abp.sitemap/` directory. To use it in your own project, add a project reference:
```xml
<ProjectReference Include="../Modules/abp.sitemap/Abp.Sitemap.Web/Abp.Sitemap.Web.csproj" />
```
## Module Configuration
After installing the package, add the module to your ABP application's module class:
```csharp
using Abp.Sitemap.Web;
[DependsOn(
typeof(SitemapWebModule), // 👈 Add sitemap module
// ... other dependencies
)]
public class YourProjectWebModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
// Configure sitemap options
Configure<SitemapOptions>(options =>
{
options.BaseUrl = "https://yourdomain.com"; // 👈 Your website URL
options.FolderPath = "Sitemaps"; // 👈 Where XML files are stored
options.WorkerPeriod = 3600000; // 👈 Regenerate every hour (in milliseconds)
});
}
}
```
> **Note:** In ABP applications, BaseUrl can be resolved from AppUrlOptions to stay consistent with environment configuration.
That's it! The module is now integrated and will automatically:
- Discover your Razor Pages
- Generate sitemap XML files on application startup
- Regenerate sitemaps in the background every hour
## Usage Examples
Let's explore practical examples of using the sitemap module. You can see complete working examples in the [AbpSitemapDemo repository](https://github.com/salihozkara/AbpSitemapDemo).
### Example 1: Mark Static Pages
The simplest way to include pages in your sitemap is using attributes:
```csharp
using Abp.Sitemap.Web.Sitemap.Sources.Page.Attributes;
namespace YourProject.Pages;
[IncludeSitemapXml] // 👈 Include in default "Main" group
public class IndexModel : PageModel
{
public void OnGet()
{
// Your page logic
}
}
[IncludeSitemapXml(Group = "Help")]
public class FaqModel : PageModel
{
public void OnGet()
{
// Your page logic
}
}
```
These pages will be automatically discovered and included in the sitemap XML files.
### Example 2: Add Dynamic Content from Database
For dynamic content like blog posts, products, or articles, create a custom sitemap source. Here's a complete example using a Book entity:
```csharp
using Abp.Sitemap.Web.Sitemap.Core;
using Abp.Sitemap.Web.Sitemap.Sources.Group;
using Volo.Abp.DependencyInjection;
namespace YourProject.Sitemaps;
public class BookSitemapSource : GroupedSitemapItemSource<Book>, ITransientDependency
{
public BookSitemapSource(
IReadOnlyRepository<Book> repository,
IAsyncQueryableExecuter executer)
: base(repository, executer, group: "Books") // 👈 Creates sitemap-Books.xml
{
Filter = x => x.IsPublished; // 👈 Only published books
}
protected override Expression<Func<Book, SitemapItem>> Selector =>
book => new SitemapItem(
book.Id.ToString(), // 👈 Unique identifier
$"/Books/Detail/{book.Id}", // 👈 URL pattern matching your route
book.LastModificationTime ?? book.CreationTime // 👈 Last modified date
)
{
ChangeFrequency = "weekly",
Priority = 0.7
};
}
```
Key points:
- Inherits from `GroupedSitemapItemSource<TEntity>`
- Specifies the entity type (`Book`)
- Defines a group name ("Books") which creates `sitemap-Books.xml`
- Uses `Filter` to include only published books
- Maps entity properties to sitemap URLs using `Selector`
- Automatically registered via `ITransientDependency`
### Example 3: Category-Based Dynamic Content
For content with categories, you can build more complex URL patterns:
```csharp
using Abp.Sitemap.Web.Sitemap.Core;
using Abp.Sitemap.Web.Sitemap.Sources.Group;
namespace YourProject.Sitemaps;
public class ArticleSitemapSource : GroupedSitemapItemSource<Article>, ITransientDependency
{
public ArticleSitemapSource(
IReadOnlyRepository<Article> repository,
IAsyncQueryableExecuter executer)
: base(repository, executer, "Articles")
{
// Multiple filter conditions
Filter = x => x.IsPublished &&
!x.IsDeleted &&
x.PublishDate <= DateTime.Now;
}
protected override Expression<Func<Article, SitemapItem>> Selector =>
article => new SitemapItem(
article.Id.ToString(),
$"/blog/{article.Category.Slug}/{article.Slug}", // 👈 Category-based URL
article.LastModificationTime ?? article.CreationTime
);
}
```
This example demonstrates:
- Multiple filter conditions for complex business logic
- Building URLs with category slugs
## Testing Your Sitemaps
After configuring the module, test your sitemap generation:
### 1. Run Your Application
```bash
dotnet run
```
The sitemaps are automatically generated on application startup.
### 2. Check Generated Files
Navigate to `{WebProject}/Sitemaps/` directory (at the root of your web project):
```
{WebProject}
└── Sitemaps/
├── sitemap.xml # Main group (static pages)
├── sitemap-Books.xml # Books from database
├── sitemap-Articles.xml # Articles from database
└── sitemap-Help.xml # Help pages
```
### 3. Verify XML Content
Open `sitemap-Books.xml` and verify the structure:
```xml
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>https://yourdomain.com/Books/Detail/3a071e39-12c9-48d7-8c1e-3b4f5c6d7e8f</loc>
<lastmod>2025-12-13</lastmod>
</url>
<url>
<loc>https://yourdomain.com/Books/Detail/7b8c9d0e-1f2a-3b4c-5d6e-7f8g9h0i1j2k</loc>
<lastmod>2025-12-10</lastmod>
</url>
</urlset>
```
### 4. Test in Browser
Visit the sitemap URLs directly (the module serves them from the root path):
- Main sitemap: `https://localhost:5001/sitemap.xml`
- Books sitemap: `https://localhost:5001/sitemap-Books.xml`
> **Note:** The sitemaps are stored in `{WebProject}/Sitemaps/` directory and served directly from the root URL.
## Advanced Configuration
### Custom Regeneration Schedule
Control when sitemaps are regenerated using cron expressions:
```csharp
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<SitemapOptions>(options =>
{
options.BaseUrl = "https://yourdomain.com";
options.WorkerCronExpression = "0 0 2 * * ?"; // 👈 Every day at 2 AM
// Or use period in milliseconds:
// options.WorkerPeriod = 7200000; // 2 hours
});
}
```
### Environment-Specific Configuration
Use different settings for development and production:
```csharp
public override void ConfigureServices(ServiceConfigurationContext context)
{
var configuration = context.Services.GetConfiguration();
var hostingEnvironment = context.Services.GetHostingEnvironment();
Configure<SitemapOptions>(options =>
{
if (hostingEnvironment.IsDevelopment())
{
options.BaseUrl = "https://localhost:5001";
options.WorkerPeriod = 300000; // 5 minutes for testing
}
else
{
options.BaseUrl = configuration["App:SelfUrl"]!;
options.WorkerPeriod = 3600000; // 1 hour in production
}
options.FolderPath = "Sitemaps";
});
}
```
### Manual Sitemap Generation
Trigger sitemap generation manually (useful for admin panels):
```csharp
using Abp.Sitemap.Web.Sitemap.Management;
public class SitemapManagementService : ITransientDependency
{
private readonly SitemapFileGenerator _generator;
public SitemapManagementService(SitemapFileGenerator generator)
{
_generator = generator;
}
[Authorize("Admin")]
public async Task RegenerateSitemapsAsync()
{
await _generator.GenerateAsync(); // 👈 Manual regeneration
}
}
```
## Real-World Use Cases
Here are practical scenarios where the sitemap module excels:
### E-Commerce Platform
```csharp
// Products grouped by category
public class ProductSitemapSource : GroupedSitemapItemSource<Product>
{
// Automatically includes all active products with stock
}
// Separate sitemap for categories
public class CategorySitemapSource : GroupedSitemapItemSource<Category>
{
// All browsable categories
}
// Brand pages
public class BrandSitemapSource : GroupedSitemapItemSource<Brand>
{
// All active brands
}
```
Result: `sitemap-Products.xml`, `sitemap-Categories.xml`, `sitemap-Brands.xml`
### Content Management System
```csharp
// Blog posts by date
public class BlogPostSitemapSource : GroupedSitemapItemSource<BlogPost>
{
// Filter by published date, priority based on view count
}
// Static CMS pages
[IncludeSitemapXml]
public class AboutUsModel : PageModel { }
```
## Best Practices
### 1. Group Related Content
Organize your sitemaps logically:
```csharp
// ✅ Good: Logical grouping
"Products", "Categories", "Brands", "Blog", "Help"
// ❌ Bad: Everything in one group
"Main" // Contains 50,000 mixed URLs
```
### 2. Use Filters Wisely
```csharp
// ✅ Good: Only published, non-deleted content
Filter = x => x.IsPublished &&
!x.IsDeleted &&
x.PublishDate <= DateTime.Now
// ❌ Bad: Including draft content
Filter = x => true // Everything included
```
### 3. Keep URLs Clean
```csharp
// ✅ Good: SEO-friendly URLs
$"/products/{product.Slug}"
$"/blog/{year}/{month}/{article.Slug}"
// ❌ Bad: Technical IDs exposed
$"/product-detail?id={product.Id}"
```
## Troubleshooting
### Sitemap Not Generated
**Problem:** No XML files in `{WebProject}/Sitemaps/`
**Solutions:**
1. Check module is added to dependencies
2. Verify `SitemapOptions.BaseUrl` is configured
3. Check application logs for errors
4. Ensure the web project directory has write permissions
### Pages Not Appearing
**Problem:** Some pages missing from sitemap
**Solutions:**
1. Verify `[IncludeSitemapXml]` attribute is present
2. Check namespace imports: `using Abp.Sitemap.Web.Sitemap.Sources.Page.Attributes;`
3. Ensure PageModel classes are public
4. Check filter conditions in custom sources
### Background Worker Not Running
**Problem:** Sitemaps not regenerating automatically
**Solutions:**
1. Check `SitemapOptions.WorkerPeriod` is set
2. Verify background workers are enabled in ABP configuration
3. Check application logs for worker errors
## Performance Considerations
### Caching Strategy
Consider adding caching for frequently accessed sitemaps:
```csharp
public class CachedSitemapFileGenerator : ITransientDependency
{
private readonly SitemapFileGenerator _generator;
private readonly IDistributedCache _cache;
public async Task<string> GetOrGenerateAsync(string group)
{
var cacheKey = $"Sitemap:{group}";
var cached = await _cache.GetStringAsync(cacheKey);
if (cached != null)
return cached;
await _generator.GenerateAsync();
// Read and cache...
}
}
```
## Conclusion
The ABP Sitemap module provides a production-ready solution for dynamic sitemap generation in ABP Framework applications. By leveraging ABP's architecture—dependency injection, repository pattern, and background workers—the module automatically discovers pages, includes dynamic content, and regenerates sitemaps without manual intervention.
Key benefits:
**Zero Configuration** for basic scenarios
**Type-Safe** attribute-based configuration
**Extensible** for complex business logic
**Performance** optimized with background processing
**SEO-Friendly** following XML sitemap standards
Whether you're building a blog, e-commerce platform, or enterprise application, this module provides a solid foundation for search engine optimization.
## Additional Resources
### Documentation
- [ABP Framework Documentation](https://abp.io/docs/latest/)
- [ABP Background Workers](https://abp.io/docs/latest/framework/infrastructure/background-workers)
- [ABP Repository Pattern](https://abp.io/docs/latest/framework/architecture/domain-driven-design/repositories)
- [ABP Dependency Injection](https://abp.io/docs/latest/framework/fundamentals/dependency-injection)
### Source Code
- [Complete Working Demo](https://github.com/salihozkara/AbpSitemapDemo) - Full implementation with examples
- [BookSitemapSource](https://github.com/salihozkara/AbpSitemapDemo/blob/master/AbpSitemapDemo/Pages/Books/Index.cshtml.cs#L23) - Entity-based source example
- [Index.cshtml](https://github.com/salihozkara/AbpSitemapDemo/blob/master/AbpSitemapDemo/Pages/Index.cshtml#L9) - Page attribute usage

1
docs/en/Community-Articles/2025-12-13-Building-Dynamic-XML-Sitemaps-With-ABP-Framework/summary.md

@ -0,0 +1 @@
Learn how to use the ABP Sitemap module for automatic XML sitemap generation in your ABP Framework applications.

BIN
docs/en/Community-Articles/2025-12-18-Announcement-AIMAnagement/cover.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

BIN
docs/en/Community-Articles/2025-12-18-Announcement-AIMAnagement/images/abp-studio-ai-management.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

BIN
docs/en/Community-Articles/2025-12-18-Announcement-AIMAnagement/images/ai-management-workspace-playground.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

BIN
docs/en/Community-Articles/2025-12-18-Announcement-AIMAnagement/images/ai-management-workspace-widget.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
docs/en/Community-Articles/2025-12-18-Announcement-AIMAnagement/images/aimanagement-workspace-geminiasopenai.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

106
docs/en/Community-Articles/2025-12-18-Announcement-AIMAnagement/post.md

@ -0,0 +1,106 @@
# Introducing the AI Management Module: Manage AI Integration Dynamically
We are excited to announce the **AI Management Module**, a powerful new module to the ABP Platform that makes managing AI capabilities in your applications easier. No need to redeploy your application, now you can configure, test, and manage your AI integrations on the fly through an intuitive user interface!
## What is the AI Management Module?
Built on top of the [ABP Framework's AI infrastructure](https://abp.io/docs/latest/framework/infrastructure/artificial-intelligence), the AI Management Module allows you to manage AI workspaces dynamically without touching your code. Whether you're building a customer support chatbot, adding AI-powered search, or creating intelligent automation workflows, this module provides everything you need to manage AI integrations through a user-friendly interface.
> **Note**: The AI Management Module is currently in **preview** and available to ABP Team or higher license holders.
## What it offers?
### Manage AI Without Redeployment
Create, configure, and update AI workspaces directly from the UI. Switch between different AI providers (OpenAI, Azure OpenAI, Ollama, etc.), change models, adjust prompts, and test configurations, all without restarting your application or deploying new code.
### Built-In Chat Interface
Test your AI workspaces immediately with the included chat interface in playground pages. Verify your configurations work correctly before using them in production. Perfect for experimenting with different models, prompts, and settings.
![AI Management Playground](./images/ai-management-workspace-playground.png)
### Flexible for Any Architecture
Whether you're building a monolith, microservices, or something in between, the module adapts to your needs:
- Host AI management directly in your application with full UI and database
- Deploy a centralized AI service that multiple applications can consume
- Use it as an API gateway pattern for your microservices
### Works with Any AI Provider
Even AI Management module doesn't implement all the providers by default, it provides extensibility options with a good abstraction for other providers like Azure, Anthropic Claude, Google Gemini, and more. Or you can directly use the OpenAI adapter with LLMs that support OpenAI API.
- Example of using Gemini as an OpenAI provider:
![Using Gemini as an OpenAI provider](./images/aimanagement-workspace-geminiasopenai.png)
You can even add your own custom AI providers: [learn how to implement a custom AI provider factory in the documentation](https://abp.io/docs/latest/modules/ai-management#implementing-custom-ai-provider-factories).
### Ready to Use Chat Widget
Drop a compact, pre-built chat widget into any page with minimal code. It includes streaming support, conversation history, and API integration for customization.
- Simple to use with minimal code
```cs
@await Component.InvokeAsync(typeof(ChatClientChatViewComponent), new ChatClientChatViewModel
{
WorkspaceName = "StoryTeller",
})
```
- And result is a working, pre-integrated widget
![AI Management Chat Widget](./images/ai-management-workspace-widget.png)
- [See the widget documentation](https://abp.io/docs/latest/modules/ai-management#client-usage-mvc-ui) for details and all parameters for customization.
### Security
Control who can manage and use AI workspaces with permission-based access control. Isolate your AI configurations by using workspaces with different permissions. Also, resource based authorization on workspaces is on the way and will be available in the next versions. It'll allow you to manage access to specific workspaces by a user or role.
## Getting Started
Installation is straightforward using the [ABP Studio](https://abp.io/studio). You can just enable **AI Management** module while creating a new project with ABP Studio and configure your preferred AI provider and model in the solution creation wizard.
![ABP Studio AI Management Solution Creation Wizard](./images/abp-studio-ai-management.png)
## Roadmap
### v10.0 ✅
- Workspace Management
- MVC UI
- Playground
- Chat History _(Client-Side)_
- Client Components
- Integration to Startup Templates
### v10.1
- Blazor UI
- Angular UI
- Resource based authorization on Workspaces
- Agent-Framework compatibility examples
### Future Goals
- Microservice templates
- MCP Support
- RAG with file upload _(md, pdf, txt)_
- Chat History _(Server-Side Conversations)_
- OpenAI Compatible Endpoints
- Tenant-Based Configuration
- Extended RAG capabilities, _(ie. providing application data as tools)_
## Ready to Get Started?
The AI Management Module is available now for ABP Team and higher license holders.
**Learn More:**
- [AI Management Module Documentation](https://abp.io/docs/latest/modules/ai-management) - All features, scenarios, and technical details.
- [AI Infrastructure Documentation](https://abp.io/docs/latest/framework/infrastructure/artificial-intelligence) - Understanding AI workspaces in the framework.
- [Usage Scenarios](https://abp.io/docs/latest/modules/ai-management#usage-scenarios) - Examples for different architectures.
---
*The AI Management Module is currently in preview. We're excited to hear your feedback as we continue to improve and add new features!*

38
docs/en/Community-Articles/2025-12-23-Referral-Program-Announcement/post.md

@ -0,0 +1,38 @@
We are happy to share some exciting news. We launched **ABP.IO Referral Program** as a way to thank our customers and community members who help introduce ABP.IO to new professionals and organizations\!
If you already use ABP.IO and believe in it, you can now get benefits by recommending it to others.
## **What is ABP.IO Referral Program?**
<img alt="Referral_Program_-3" src="https://github.com/user-attachments/assets/b4957516-023d-4ea4-95db-173c6fa1e34d" />
ABP.IO Referral Program rewards people who bring new customers to ABP.IO.
When someone you refer purchases an ABP.IO license, you earn a commission as a thank-you for your contribution.
*\*This referral program is available to users who have an organization.*
## **What Benefits Do You Get?**
* Earn **5% commission** on the total sales price of each new license you refer
* Get rewarded for sharing a platform you already know and trust
## **Who Can Join?**
You can participate if:
* You are an existing ABP.IO customer who has purchased a license before
* You are a license owner or a developer
* Your license is active or expired
* The purchase is not made by your own company
## **Start Referring Today**
If you are interested in joining the program, you can get started right away.
👉 [**Start Referring Now**](https://abp.io/my-referrals)
Thank you for being part of our community. Your support helps us grow and now it pays back.

26
docs/en/docs-nav.json

@ -563,7 +563,11 @@
{
"text": "Microsoft.Extensions.AI",
"path": "framework/infrastructure/artificial-intelligence/microsoft-extensions-ai.md"
},
},
{
"text": "Agent Framework",
"path": "framework/infrastructure/artificial-intelligence/microsoft-agent-framework.md"
},
{
"text": "Semantic Kernel",
"path": "framework/infrastructure/artificial-intelligence/microsoft-semantic-kernel.md"
@ -1561,6 +1565,10 @@
"text": "SSR Configuration",
"path": "framework/ui/angular/ssr-configuration.md"
},
{
"text": "AI Tools Configuration",
"path": "framework/ui/angular/ai-config.md"
},
{
"text": "PWA Configuration",
"path": "framework/ui/angular/pwa-configuration.md"
@ -1861,6 +1869,10 @@
"text": "Overriding the User Interface",
"path": "framework/architecture/modularity/extending/overriding-user-interface.md"
},
{
"text": "How to Override LeptonX CSS Variables",
"path": "framework/ui/common/leptonx-css-variables.md"
},
{
"text": "Utilities",
"items": [
@ -2294,6 +2306,10 @@
"text": "Helm Charts and Kubernetes",
"path": "solution-templates/microservice/helm-charts-and-kubernetes.md"
},
{
"text": ".NET Aspire Integration",
"path": "solution-templates/microservice/aspire-integration.md"
},
{
"text": "Guides",
"items": [
@ -2355,13 +2371,17 @@
"path": "modules/account-pro.md",
"isIndex": true
},
{
"text": "Idle Session Timeout",
"path": "modules/account/idle-session-timeout.md"
},
{
"text": "Tenant impersonation & User impersonation",
"path": "modules/account/impersonation.md"
},
{
"text": "Idle Session Timeout",
"path": "modules/account/idle-session-timeout.md"
"text": "Web Authentication API (WebAuthn) passkeys",
"path": "modules/account/passkey.md"
}
]
},

4
docs/en/framework/architecture/multi-tenancy/index.md

@ -47,7 +47,7 @@ ABP supports all the following approaches to store the tenant data in the databa
- **Database per Tenant**: Every tenant has a separate, dedicated database to store the data related to that tenant.
- **Hybrid**: Some tenants share a single database while some tenants may have their own databases.
[Saas module (PRO)](../../../modules/saas.md) allows you to set a connection string for any tenant (as optional), so you can achieve any of the approaches.
[SaaS module (PRO)](../../../modules/saas.md) allows you to set a connection string for any tenant (as optional), so you can achieve any of the approaches.
> You can see the community article *[Multi-Tenancy with Separate Databases in .NET and ABP Framework](https://abp.io/community/articles/multitenancy-with-separate-databases-in-dotnet-and-abp-51nvl4u9)* for more details about different database architectures with practical implementation details.
@ -466,7 +466,7 @@ The [Tenant Management module](../../../modules/tenant-management.md) provides a
### A note about separate database per tenant approach in open source version
While ABP fully supports this option, managing connection strings of tenants from the UI is not available in open source version. You need to have [Saas module (PRO)](../../../modules/saas.md).
While ABP fully supports this option, managing connection strings of tenants from the UI is not available in open source version. You need to have [SaaS module (PRO)](../../../modules/saas.md).
Alternatively, you can implement this feature yourself by customizing the tenant management module and tenant application service to create and migrate the database on the fly.
## See Also

2
docs/en/framework/fundamentals/dependency-injection.md

@ -264,7 +264,7 @@ public class TaxAppService : ApplicationService
``TaxAppService`` gets ``ITaxCalculator`` in its constructor. The dependency injection system automatically provides the requested service at runtime.
Constructor injection is preffered way of injecting dependencies to a class. In that way, the class can not be constructed unless all constructor-injected dependencies are provided. Thus, the class explicitly declares it's required services.
Constructor injection is preferred way of injecting dependencies to a class. In that way, the class can not be constructed unless all constructor-injected dependencies are provided. Thus, the class explicitly declares it's required services.
### Property Injection

4
docs/en/framework/fundamentals/exception-handling.md

@ -88,7 +88,7 @@ Error **details** in an optional field of the JSON error message. Thrown `Except
}
````
`AbpValidationException` implements the `IHasValidationErrors` interface and it is automatically thrown by the framework when a request input is not valid. So, usually you don't need to deal with validation errors unless you have higly customised validation logic.
`AbpValidationException` implements the `IHasValidationErrors` interface and it is automatically thrown by the framework when a request input is not valid. So, usually you don't need to deal with validation errors unless you have highly customized validation logic.
### Logging
@ -289,7 +289,7 @@ The `IHttpExceptionStatusCodeFinder` is used to automatically determine the HTTP
### Custom Mappings
Automatic HTTP status code determination can be overrided by custom mappings. For example:
Automatic HTTP status code determination can be overridden by custom mappings. For example:
````C#
services.Configure<AbpExceptionHttpStatusCodeOptions>(options =>

2
docs/en/framework/fundamentals/validation.md

@ -121,7 +121,7 @@ namespace Acme.BookStore
#### Enabling/Disabling Validation
You can use the `[DisableValidation]` to disable it for methods, classs and properties.
You can use the `[DisableValidation]` to disable it for methods, classes and properties.
````csharp
[DisableValidation]

4
docs/en/framework/infrastructure/artificial-intelligence/index.md

@ -27,12 +27,14 @@ abp add-package Volo.Abp.AI
The `Volo.Abp.AI` package provides integration with the following libraries:
* [Microsoft.Extensions.AI](https://learn.microsoft.com/en-us/dotnet/ai/microsoft-extensions-ai)
* [Microsoft.Agents.AI (Agent Framework)](https://learn.microsoft.com/en-us/agent-framework/overview/agent-framework-overview)
* [Microsoft.SemanticKernel](https://learn.microsoft.com/en-us/semantic-kernel/overview/)
The Microsoft.Extensions.AI library is suggested for library developers to keep the library dependency minimum and simple (since it provides basic abstractions and fundamental AI provider integrations), while Semantic Kernel is suggested for applications that need rich and advanced AI integration features.
The **Microsoft.Extensions.AI** library is suggested for library developers to keep the library dependency minimum and simple (since it provides basic abstractions and fundamental AI provider integrations). For applications, **Microsoft Agent Framework** is the recommended choice as it combines the best of both AutoGen and Semantic Kernel (it's direct successor of these two frameworks), offering simple abstractions for single- and multi-agent patterns along with advanced features like thread-based state management, type safety, filters, and telemetry. **Semantic Kernel** can still be used if you need its specific AI integration features.
Check the following documentation to learn how to use these libraries with the ABP integration:
- [ABP Microsoft.Extensions.AI integration](./microsoft-extensions-ai.md)
- [ABP Microsoft.Agents.AI (Agent Framework) integration](./microsoft-agent-framework.md)
- [ABP Microsoft.SemanticKernel integration](./microsoft-semantic-kernel.md)

218
docs/en/framework/infrastructure/artificial-intelligence/microsoft-agent-framework.md

@ -0,0 +1,218 @@
# Microsoft.Agents.AI (Agent Framework)
[Microsoft Agent Framework](https://learn.microsoft.com/en-us/agent-framework/overview/agent-framework-overview) is an open-source development kit for **building AI agents** and **multi-agent workflows**. It is the direct successor to both *AutoGen* and [*Semantic Kernel*](./microsoft-semantic-kernel.md), combining their strengths while adding new capabilities, and is the suggested framework for building AI agent applications. This documentation is about the usage of this library with ABP Framework. Make sure you have read the [Artificial Intelligence](./index.md) documentation before reading this documentation.
## Usage
**Microsoft Agent Framework** works on top of `IChatClient` from **Microsoft.Extensions.AI**. After obtaining an `IChatClient` instance, you can create an AI agent using the `CreateAIAgent` extension method:
```csharp
using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;
public class MyService
{
private readonly IChatClient _chatClient;
public MyService(IChatClient chatClient)
{
_chatClient = chatClient;
}
public async Task<string> GetResponseAsync(string userMessage)
{
AIAgent agent = _chatClient.CreateAIAgent(
instructions: "You are a helpful assistant that provides concise answers."
);
AgentRunResponse response = await agent.RunAsync(userMessage);
return response.Text;
}
}
```
You can also use `IChatClientAccessor` to access the `IChatClient` in scenarios where AI capabilities are **optional**, such as when developing a module or a service that may use AI capabilities optionally:
```csharp
using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;
using Volo.Abp.AI;
public class MyService
{
private readonly IChatClientAccessor _chatClientAccessor;
public MyService(IChatClientAccessor chatClientAccessor)
{
_chatClientAccessor = chatClientAccessor;
}
public async Task<string> GetResponseAsync(string userMessage)
{
var chatClient = _chatClientAccessor.ChatClient;
if (chatClient is null)
{
return "No chat client configured";
}
AIAgent agent = chatClient.CreateAIAgent(
instructions: "You are a helpful assistant that provides concise answers."
);
var response = await agent.RunAsync(userMessage);
return response.Text;
}
}
```
### Workspaces
Workspaces are a way to configure isolated AI configurations for a named scope. You can define a workspace by decorating a class with the `WorkspaceNameAttribute` attribute that carries the workspace name.
- Workspace names must be unique.
- Workspace names cannot contain spaces _(use underscores or camelCase)_.
- Workspace names are case-sensitive.
```csharp
using Volo.Abp.AI;
[WorkspaceName("CommentSummarization")]
public class CommentSummarization
{
}
```
> [!NOTE]
> If you don't specify the workspace name, the full name of the class will be used as the workspace name.
You can resolve generic versions of `IChatClient` and `IChatClientAccessor` services for a specific workspace as generic arguments. If Chat Client is not configured for a workspace, you will get `null` from the accessor services. You should check the accessor before using it. This applies only for specified workspaces. Another workspace may have a configured Chat Client.
`IChatClient<TWorkSpace>` or `IChatClientAccessor<TWorkSpace>` can be resolved to access a specific workspace's chat client. This is a typed chat client and can be configured separately from the default chat client.
Example of resolving a typed chat client for a workspace:
```csharp
using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;
using Volo.Abp.AI;
public class MyService
{
private readonly IChatClient<CustomerSupport> _chatClient;
public MyService(IChatClient<CustomerSupport> chatClient)
{
_chatClient = chatClient;
}
public async Task<string> GetResponseAsync(string userMessage)
{
AIAgent agent = _chatClient.CreateAIAgent(
instructions: "You are a customer support assistant. Be polite and helpful."
);
var response = await agent.RunAsync(userMessage);
return response.Text;
}
}
```
Example of resolving a typed chat client accessor for a workspace:
```csharp
using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;
using Volo.Abp.AI;
public class MyService
{
private readonly IChatClientAccessor<CustomerSupport> _chatClientAccessor;
public MyService(IChatClientAccessor<CustomerSupport> chatClientAccessor)
{
_chatClientAccessor = chatClientAccessor;
}
public async Task<string> GetResponseAsync(string userMessage)
{
var chatClient = _chatClientAccessor.ChatClient;
if (chatClient is null)
{
return "No chat client configured";
}
AIAgent agent = chatClient.CreateAIAgent(
instructions: "You are a customer support assistant. Be polite and helpful."
);
var response = await agent.RunAsync(userMessage);
return response.Text;
}
}
```
## Configuration
**Microsoft Agent Framework** uses `IChatClient` from **Microsoft.Extensions.AI** as its foundation. Therefore, the configuration process for workspaces is the same as described in the [Microsoft.Extensions.AI documentation](./microsoft-extensions-ai.md#configuration).
You need to configure the Chat Client for your workspace using `AbpAIWorkspaceOptions`, and then you can use the `CreateAIAgent` extension method to create AI agents from the configured chat client.
To configure a chat client, you'll need a LLM provider package such as [Microsoft.Extensions.AI.OpenAI](https://www.nuget.org/packages/Microsoft.Extensions.AI.OpenAI) or [OllamaSharp](https://www.nuget.org/packages/OllamaSharp/).
_The following example requires [OllamaSharp](https://www.nuget.org/packages/OllamaSharp/) package to be installed._
Demonstration of the default workspace configuration:
```csharp
[DependsOn(typeof(AbpAIModule))]
public class MyProjectModule : AbpModule
{
public override void PreConfigureServices(ServiceConfigurationContext context)
{
PreConfigure<AbpAIWorkspaceOptions>(options =>
{
options.Workspaces.ConfigureDefault(configuration =>
{
configuration.ConfigureChatClient(chatClientConfiguration =>
{
chatClientConfiguration.Builder = new ChatClientBuilder(
sp => new OllamaApiClient("http://localhost:11434", "mistral")
);
});
});
});
}
}
```
Demonstration of the isolated workspace configuration:
```csharp
[DependsOn(typeof(AbpAIModule))]
public class MyProjectModule : AbpModule
{
public override void PreConfigureServices(ServiceConfigurationContext context)
{
PreConfigure<AbpAIWorkspaceOptions>(options =>
{
options.Workspaces.Configure<CustomerSupport>(configuration =>
{
configuration.ConfigureChatClient(chatClientConfiguration =>
{
chatClientConfiguration.Builder = new ChatClientBuilder(
sp => new OllamaApiClient("http://localhost:11434", "mistral")
);
});
});
});
}
}
```
## See Also
- [Usage of Microsoft.Extensions.AI](./microsoft-extensions-ai.md)
- [Usage of Semantic Kernel](./microsoft-semantic-kernel.md)
- [Microsoft Agent Framework Overview](https://learn.microsoft.com/en-us/agent-framework/overview/agent-framework-overview)
- [AI Samples for .NET](https://learn.microsoft.com/en-us/samples/dotnet/ai-samples/ai-samples/)

1
docs/en/framework/infrastructure/artificial-intelligence/microsoft-extensions-ai.md

@ -172,5 +172,6 @@ public class MyProjectModule : AbpModule
## See Also
- [Usage of Agent Framework](./microsoft-agent-framework.md)
- [Usage of Semantic Kernel](./microsoft-semantic-kernel.md)
- [AI Samples for .NET](https://learn.microsoft.com/en-us/samples/dotnet/ai-samples/ai-samples/)

2
docs/en/framework/infrastructure/audit-logging.md

@ -215,7 +215,7 @@ public class MyUser : Entity<Guid>
public string Email { get; set; }
[DisableAuditing] //Ignore the Passoword on audit logging
[DisableAuditing] //Ignore the Password on audit logging
public string Password { get; set; }
}
````

2
docs/en/framework/infrastructure/background-workers/hangfire.md

@ -61,7 +61,7 @@ After you have installed these NuGet packages, you need to configure your projec
var configuration = context.Services.GetConfiguration();
var hostingEnvironment = context.Services.GetHostingEnvironment();
//... other configarations.
//... other configurations.
ConfigureHangfire(context, configuration);
}

2
docs/en/framework/infrastructure/background-workers/quartz.md

@ -43,7 +43,7 @@ public class YourModule : AbpModule
}
````
> Quartz background worker integration provided `QuartzPeriodicBackgroundWorkerAdapter` to adapt `PeriodicBackgroundWorkerBase` and `AsyncPeriodicBackgroundWorkerBase` derived class. So, you can still fllow the [background workers document](../background-workers) to define the background worker.
> Quartz background worker integration provided `QuartzPeriodicBackgroundWorkerAdapter` to adapt `PeriodicBackgroundWorkerBase` and `AsyncPeriodicBackgroundWorkerBase` derived class. So, you can still follow the [background workers document](../background-workers) to define the background worker.
## Configuration

2
docs/en/framework/infrastructure/current-user.md

@ -101,7 +101,7 @@ For a web application, it gets the `User` property of the current `HttpContext`.
### Basic Usage
You can inject `ICurrentPrincipalAccessor` and use the `Principal` property to the the current principal:
You can inject `ICurrentPrincipalAccessor` and use the `Principal` property to get the current principal:
````csharp
public class MyService : ITransientDependency

4
docs/en/framework/infrastructure/emailing.md

@ -235,7 +235,7 @@ The final rendered message was shown above.
You typically want to replace the standard templates with your own ones, so you can prepare a branded email messages. To do that, you can use the power of the [virtual file system](../infrastructure/virtual-file-system.md) (VFS) or replace them in your own template definition provider.
Pathes of the templates in the virtual file system are shown below:
Paths of the templates in the virtual file system are shown below:
* `/Volo/Abp/Emailing/Templates/Layout.tpl`
* `/Volo/Abp/Emailing/Templates/Message.tpl`
@ -250,7 +250,7 @@ See the [text templating system](./text-templating) document for details.
## NullEmailSender
`NullEmailSender` is a built-in class that implements the `IEmailSender`, but writes email contents to the [standard log system](../fundamentals/logging.md), rathen than actually sending the emails.
`NullEmailSender` is a built-in class that implements the `IEmailSender`, but writes email contents to the [standard log system](../fundamentals/logging.md), rather than actually sending the emails.
This class can be useful especially in development time where you generally don't want to send real emails. The [application startup template](../../solution-templates/layered-web-application) already uses this class in the **DEBUG mode** with the following configuration in the domain layer:

23
docs/en/framework/infrastructure/features.md

@ -395,8 +395,31 @@ There are three pre-defined value providers, executed by the given order:
* `TenantFeatureValueProvider` tries to get if the feature value is explicitly set for the **current tenant**.
* `EditionFeatureValueProvider` tries to get the feature value for the current edition. Edition Id is obtained from the current principal identity (`ICurrentPrincipalAccessor`) with the claim name `editionid` (a constant defined as`AbpClaimTypes.EditionId`). Editions are not implemented for the [tenant management](../../modules/tenant-management.md) module. You can implement it yourself or consider to use the [SaaS module](https://abp.io/modules/Volo.Saas) of the ABP Commercial.
* `ConfigurationFeatureValueProvider`: Gets the value from the [IConfiguration service](../fundamentals/configuration.md).
* `DefaultValueFeatureValueProvider` gets the default value of the feature.
#### Feature Values in the Application Configuration
The `ConfigurationFeatureValueProvider` reads the feature values from the `IConfiguration` service, which can read values from the `appsettings.json` by default. So, the easiest way to configure feature values is to define them in the `appsettings.json` file.
For example, you can configure feature values as shown below:
````json
{
"Features": {
"MyApp.Reporting": "true",
"MyApp.PdfReporting": "true",
"MyApp.MaxProductCount": "50"
}
}
````
Feature values should be configured under the `Features` section as like in this example.
> `IConfiguration` is an .NET Core service and it can read values not only from the `appsettings.json`, but also from the environment, user secrets... etc. See [Microsoft's documentation](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/) for more.
#### Custom Feature Value Providers
You can write your own provider by inheriting the `FeatureValueProvider`.
**Example: Enable all features for a user with "SystemAdmin" as a "User_Type" claim value**

2
docs/en/framework/infrastructure/settings.md

@ -43,7 +43,7 @@ ABP automatically discovers this class and registers the setting definitions.
* **DefaultValue**: A setting may have a default value.
* **DisplayName**: A localizable string that can be used to show the setting name on the UI.
* **Description**: A localizable string that can be used to show the setting description on the UI.
* **IsVisibleToClients**: A boolean value indicates that whether this setting value is available in the client side or not. Default value is false to prevent accidently publishing an internal critical setting value.
* **IsVisibleToClients**: A boolean value indicates that whether this setting value is available in the client side or not. Default value is false to prevent accidentally publishing an internal critical setting value.
* **IsInherited**: A boolean value indicates that whether this setting value is inherited from other providers or not. Default value is true and fallbacks to the next provider if the setting value was not set for the requested provider (see the setting value providers section for more).
* **IsEncrypted**: A boolean value indicates that whether this setting value should be encrypted on save and decrypted on read. It makes possible to secure the setting value in the database.
* **Providers**: Can be used to restrict providers available for a particular setting (see the setting value providers section for more).

262
docs/en/framework/ui/angular/ai-config.md

@ -0,0 +1,262 @@
```json
//[doc-seo]
{
"Description": "Learn how to configure AI-powered development tools for ABP Framework Angular applications with automatic setup for Claude, Cursor, Copilot, Gemini, Junie, and Windsurf."
}
```
# AI Configuration
ABP Framework provides an **AI Configuration Generator** that helps developers set up AI-powered coding assistants for their Angular applications. This schematic automatically generates configuration files for popular AI tools with pre-configured ABP best practices and guidelines.
## Overview
The AI Configuration Generator is an Angular schematic that creates standardized configuration files for various AI development tools. These configurations include:
- ABP Framework coding standards and best practices
- Angular development guidelines
- Project-specific rules and conventions
- Full-stack development patterns (ABP .NET + Angular)
## Supported AI Tools
The generator supports the following AI coding assistants:
- **Claude** - Creates `.claude/CLAUDE.md` configuration file
- **Copilot** - Creates `.github/copilot-instructions.md` configuration file
- **Cursor** - Creates `.cursor/rules/cursor.mdc` configuration file
- **Gemini** - Creates `.gemini/GEMINI.md` configuration file
- **Junie** - Creates `.junie/guidelines.md` configuration file
- **Windsurf** - Creates `.windsurf/rules/guidelines.md` configuration file
## Usage
### Basic Usage
Generate AI configuration for a single tool:
```bash
ng g @abp/ng.schematics:ai-config --tool=claude
```
### Multiple Tools
Generate configurations for multiple AI tools at once:
```bash
# Comma-separated
ng g @abp/ng.schematics:ai-config --tool=claude,cursor,copilot
# Space-separated (with quotes)
ng g @abp/ng.schematics:ai-config --tool="claude cursor gemini"
# Multiple --tool flags
ng g @abp/ng.schematics:ai-config --tool=claude --tool=cursor --tool=gemini
```
### Target Specific Project
By default, configurations are generated at the workspace root. To target a specific project:
```bash
ng g @abp/ng.schematics:ai-config --tool=claude --target-project=my-app
```
This creates the configuration files in the `my-app` project root directory.
### Overwrite Existing Files
If configuration files already exist, use the `--overwrite` flag to replace them:
```bash
ng g @abp/ng.schematics:ai-config --tool=cursor --overwrite
```
## Schema Options
The AI Configuration Generator accepts the following options:
### tool
- **Type:** `string`
- **Required:** Yes
- **Description:** Comma-separated list of AI tools to generate configurations for
- **Valid values:** `claude`, `copilot`, `cursor`, `gemini`, `junie`, `windsurf`
- **Example:** `"claude,cursor,copilot"`
### targetProject
- **Type:** `string`
- **Required:** No
- **Description:** The name of the target project in your workspace
- **Default:** Workspace root (`/`)
- **Example:** `"my-angular-app"`
### overwrite
- **Type:** `boolean`
- **Required:** No
- **Default:** `false`
- **Description:** Whether to overwrite existing configuration files
## Configuration Content
All generated configuration files include comprehensive guidelines for:
### General Principles
- Clear separation between backend (ABP/.NET) and frontend (Angular) layers
- Modular architecture patterns
- Official ABP documentation references
- Readability, maintainability, and performance standards
### ABP / .NET Development Rules
- Standard folder structure (`*.Application`, `*.Domain`, `*.EntityFrameworkCore`, `*.HttpApi`)
- C# coding conventions and naming patterns
- Modern C# features (records, pattern matching, null-coalescing)
- ABP module integration (Permissions, Settings, Audit Logging)
- Error handling and validation patterns
### Angular Development Rules
- Angular coding style and best practices
- Component architecture patterns
- Reactive programming with RxJS
- ABP Angular package usage (`@abp/ng.core`, `@abp/ng.theme.shared`)
- State management and service patterns
### Performance and Testing
- Performance optimization techniques
- Unit testing and integration testing guidelines
- Best practices for both backend and frontend
## Examples
### Example 1: Setup Claude for Development
```bash
ng g @abp/ng.schematics:ai-config --tool=claude
```
Output:
```
🚀 Generating AI configuration files...
📁 Target path: /
🤖 Selected tools: claude
✅ AI configuration files generated successfully!
📝 Generated files:
- .claude/CLAUDE.md
💡 Tip: Restart your IDE or AI tool to apply the new configurations.
```
### Example 2: Setup Multiple Tools for a Project
```bash
ng g @abp/ng.schematics:ai-config --tool="cursor,copilot,gemini" --target-project=acme-app
```
Output:
```
🚀 Generating AI configuration files...
📁 Target path: /acme-app
🤖 Selected tools: cursor, copilot, gemini
✅ AI configuration files generated successfully!
📝 Generated files:
- /acme-app/.cursor/rules/cursor.mdc
- /acme-app/.github/copilot-instructions.md
- /acme-app/.gemini/GEMINI.md
💡 Tip: Restart your IDE or AI tool to apply the new configurations.
```
### Example 3: Update Existing Configuration
```bash
ng g @abp/ng.schematics:ai-config --tool=windsurf --overwrite
```
This will regenerate the Windsurf configuration file even if it already exists.
## File Structure
After running the generator, your project will have configuration files in their respective directories:
```
your-project/
├── .claude/
│ └── CLAUDE.md # Claude AI configuration
├── .cursor/
│ └── rules/
│ └── cursor.mdc # Cursor AI configuration
├── .github/
│ └── copilot-instructions.md # GitHub Copilot configuration
├── .gemini/
│ └── GEMINI.md # Gemini AI configuration
├── .junie/
│ └── guidelines.md # Junie AI configuration
└── .windsurf/
└── rules/
└── guidelines.md # Windsurf AI configuration
```
## Best Practices
1. **Generate Early**: Set up AI configurations at the beginning of your project to ensure consistent code quality from the start.
2. **Multiple Tools**: If your team uses different AI assistants, generate configurations for all of them to maintain consistency across the team.
3. **Version Control**: Commit the generated configuration files to your repository so all team members benefit from the same AI guidelines.
4. **Keep Updated**: When ABP releases new best practices or your project evolves, regenerate configurations with the `--overwrite` flag.
5. **Project-Specific**: For monorepos or multi-project workspaces, use `--target-project` to create project-specific configurations.
## Troubleshooting
### Configuration File Already Exists
If you see a warning that a configuration file already exists:
```
⚠️ Configuration file already exists: .claude/CLAUDE.md
Use --overwrite flag to replace existing files.
```
Add the `--overwrite` flag to replace it:
```bash
ng g @abp/ng.schematics:ai-config --tool=claude --overwrite
```
### Invalid Tool Name
If you specify an invalid tool name:
```
Invalid AI tool(s): chatgpt. Valid options are: claude, copilot, cursor, gemini, junie, windsurf
```
Make sure to use only the supported tool names listed above.
### No Tools Selected
If you run the command without specifying any tools:
```bash
ng g @abp/ng.schematics:ai-config
```
You'll see usage examples and available tools:
```
ℹ️ No AI tools selected. Skipping configuration generation.
💡 Usage examples:
ng g @abp/ng.schematics:ai-config --tool=claude,cursor
ng g @abp/ng.schematics:ai-config --tool="claude, cursor"
ng g @abp/ng.schematics:ai-config --tool=gemini --tool=cursor
ng g @abp/ng.schematics:ai-config --tool=gemini --target-project=my-app
Available tools: claude, copilot, cursor, gemini, junie, windsurf
```

384
docs/en/framework/ui/angular/ssr-configuration.md

@ -1,13 +1,13 @@
```json
//[doc-seo]
{
"Description": "Learn how to configure Server-Side Rendering (SSR) for your Angular application in the ABP Framework to improve performance and SEO."
"Description": "Learn how to configure Server-Side Rendering (SSR) for your Angular application in the ABP Framework to improve performance and SEO."
}
```
# SSR Configuration
[Server-Side Rendering (SSR)](https://angular.io/guide/ssr) is a process that involves rendering pages on the server, resulting in initial HTML content that contains the page state. This allows the browser to show the page to the user immediately, before the JavaScript bundles are downloaded and executed.
[Server-Side Rendering (SSR)](https://angular.dev/guide/ssr) is a process that involves rendering pages on the server, resulting in initial HTML content that contains the page state. This allows the browser to show the page to the user immediately, before the JavaScript bundles are downloaded and executed.
SSR improves the **performance** (First Contentful Paint) and **SEO** (Search Engine Optimization) of your application.
@ -105,10 +105,10 @@ If your project uses the **Webpack Builder** (`@angular-devkit/build-angular:bro
```typescript
import {
AngularNodeAppEngine,
createNodeRequestHandler,
isMainModule,
writeResponseToNodeResponse,
AngularNodeAppEngine,
createNodeRequestHandler,
isMainModule,
writeResponseToNodeResponse,
} from '@angular/ssr/node';
import express from 'express';
import { dirname, resolve } from 'node:path';
@ -128,28 +128,28 @@ const angularApp = new AngularNodeAppEngine();
* Serve static files from /browser
*/
app.use(
express.static(browserDistFolder, {
maxAge: '1y',
index: false,
redirect: false,
}),
express.static(browserDistFolder, {
maxAge: '1y',
index: false,
redirect: false,
}),
);
/**
* Handle all other requests by rendering the Angular application.
*/
app.use((req, res, next) => {
angularApp
.handle(req)
.then(response => {
if (response) {
res.cookie('ssr-init', 'true', {...secureCookie, httpOnly: false});
return writeResponseToNodeResponse(response, res);
} else {
return next()
}
})
.catch(next);
angularApp
.handle(req)
.then(response => {
if (response) {
res.cookie('ssr-init', 'true', {...secureCookie, httpOnly: false});
return writeResponseToNodeResponse(response, res);
} else {
return next()
}
})
.catch(next);
});
// ... (Start server logic)
@ -162,10 +162,10 @@ export const reqHandler = createNodeRequestHandler(app);
import { RenderMode, ServerRoute } from '@angular/ssr';
export const serverRoutes: ServerRoute[] = [
{
path: '**',
renderMode: RenderMode.Server
}
{
path: '**',
renderMode: RenderMode.Server
}
];
```
@ -180,16 +180,16 @@ import { serverRoutes } from './app.routes.server';
import { SSR_FLAG } from '@abp/ng.core';
const serverConfig: ApplicationConfig = {
providers: [
provideAppInitializer(() => {
const platformId = inject(PLATFORM_ID);
const transferState = inject<TransferState>(TransferState);
if (isPlatformServer(platformId)) {
transferState.set(SSR_FLAG, true);
}
}),
provideServerRendering(withRoutes(serverRoutes)),
],
providers: [
provideAppInitializer(() => {
const platformId = inject(PLATFORM_ID);
const transferState = inject<TransferState>(TransferState);
if (isPlatformServer(platformId)) {
transferState.set(SSR_FLAG, true);
}
}),
provideServerRendering(withRoutes(serverRoutes)),
],
};
export const config = mergeApplicationConfig(appConfig, serverConfig);
@ -237,13 +237,177 @@ The schematic installs `openid-client` to handle authentication on the server si
> Ensure your OpenID Connect configuration (in `environment.ts` or `app.config.ts`) is compatible with the server environment.
## 5. Deployment
## 5. Render Modes & Hybrid Rendering
To deploy your Angular SSR application to a production server, follow these steps:
Angular 20 provides different rendering modes that you can configure per route in the `app.routes.server.ts` file to optimize performance and SEO.
### 5.1. Build the Application
### 5.1. Available Render Modes
```typescript
import { RenderMode, ServerRoute } from '@angular/ssr';
export const serverRoutes: ServerRoute[] = [
// Server-Side Rendering - renders on every request
{
path: 'dashboard',
renderMode: RenderMode.Server
},
// Prerender (SSG) - renders at build time
{
path: 'about',
renderMode: RenderMode.Prerender
},
// Client-Side Rendering - renders only in browser
{
path: 'admin/**',
renderMode: RenderMode.Client
},
// Default fallback
{
path: '**',
renderMode: RenderMode.Server
}
];
```
#### RenderMode.Server (SSR)
Renders HTML on every request. Best for dynamic content, personalized pages, and pages requiring authentication.
#### RenderMode.Prerender (SSG)
Generates static HTML at build time. Best for marketing pages, blog posts, and content that doesn't change frequently.
For dynamic routes, use `getPrerenderParams`:
```typescript
{
path: 'blog/:slug',
renderMode: RenderMode.Prerender,
getPrerenderParams: async () => {
const posts = await fetchBlogPosts();
return posts.map(post => ({ slug: post.slug }));
}
}
```
#### RenderMode.Client (CSR)
Traditional client-side rendering. Best for highly interactive applications and admin panels that don't need SEO.
### 5.2. Hybrid Rendering
Combine different modes in one application for optimal results:
```typescript
export const serverRoutes: ServerRoute[] = [
// Static pages
{ path: '', renderMode: RenderMode.Prerender },
{ path: 'about', renderMode: RenderMode.Prerender },
Run the build command to generate the production artifacts:
// Dynamic pages
{ path: 'account', renderMode: RenderMode.Server },
{ path: 'orders', renderMode: RenderMode.Server },
// Admin area
{ path: 'admin/**', renderMode: RenderMode.Client },
];
```
## 6. Hydration
Hydration is the process where Angular attaches to server-rendered HTML and makes it interactive. The ABP schematic automatically configures hydration for your application.
### 6.1. Common Hydration Issues
**Problem: Browser APIs on Server**
```typescript
// ❌ Bad - will fail on server
const width = window.innerWidth;
// ✅ Good - check platform
import { isPlatformBrowser } from '@angular/common';
import { PLATFORM_ID, inject } from '@angular/core';
export class MyComponent {
platformId = inject(PLATFORM_ID);
getWidth() {
if (isPlatformBrowser(this.platformId)) {
return window.innerWidth;
}
return 0;
}
}
```
**Problem: Random or Time-Based Values**
```typescript
// ❌ Bad - generates different values on server and client
id = Math.random();
currentTime = new Date();
// ✅ Good - use TransferState for consistent data
import { TransferState, makeStateKey } from '@angular/core';
const TIME_KEY = makeStateKey<string>('time');
constructor(private transferState: TransferState) {
if (isPlatformServer(this.platformId)) {
this.transferState.set(TIME_KEY, new Date().toISOString());
} else {
this.time = this.transferState.get(TIME_KEY, new Date().toISOString());
}
}
```
**Enable Debug Tracing:**
```typescript
// app.config.ts
import { provideClientHydration, withDebugTracing } from '@angular/platform-browser';
export const appConfig: ApplicationConfig = {
providers: [
provideClientHydration(withDebugTracing()),
]
};
```
## 7. Environment Variables
Configure your SSR application using environment variables in `server.ts`:
```typescript
// server.ts
const PORT = process.env['PORT'] || 4000;
const HOST = process.env['HOST'] || 'localhost';
// Start the server
if (isMainModule(import.meta.url)) {
app.listen(PORT, () => {
console.log(`Server running on http://${HOST}:${PORT}`);
});
}
```
For production, set environment variables:
```bash
# .env file or environment configuration
NODE_ENV=production
PORT=4000
HOST=0.0.0.0
API_URL=https://api.yourdomain.com
```
## 8. Deployment
To deploy your Angular SSR application to a production server:
### 8.1. Build the Application
```shell
yarn build
@ -251,30 +415,152 @@ yarn build
yarn run build:ssr
```
### 5.2. Prepare Artifacts
### 8.2. Prepare Artifacts
After the build is complete, you will find the output in the `dist` folder.
For the **Application Builder**, the output structure typically looks like this:
Copy the `dist/MyProjectName` folder to your server:
```
dist/MyProjectName/
├── browser/ # Client-side bundles
└── server/ # Server-side bundles and entry point (server.mjs)
└── server/ # Server-side bundles (server.mjs)
```
You need to copy the entire `dist/MyProjectName` folder to your server.
### 8.3. Install Production Dependencies
On your server, install only the required dependencies (schematic already added them to package.json):
```shell
npm install --production
```
### 5.3. Run the Server
Required dependencies:
- `express`: Web server framework
- `openid-client`: Authentication support
On your server, navigate to the folder where you copied the artifacts and run the server using Node.js:
### 8.4. Run the Server
**Development/Testing:**
```shell
node server/server.mjs
```
> [!TIP]
> It is recommended to use a process manager like [PM2](https://pm2.keymetrics.io/) to keep your application alive and handle restarts.
**Production (with PM2):**
Use [PM2](https://pm2.keymetrics.io/) to keep your application alive and manage restarts:
```shell
npm install -g pm2
pm2 start server/server.mjs --name "my-app"
pm2 startup # Configure PM2 to start on boot
pm2 save # Save current process list
```
## 9. Troubleshooting
### 9.1. "Window/Document is not defined"
Browser APIs don't exist on the server. Always check the platform:
```typescript
import { isPlatformBrowser } from '@angular/common';
if (isPlatformBrowser(this.platformId)) {
// Safe to use window, document, localStorage, etc.
}
```
### 9.2. "LocalStorage is not defined"
ABP Core provides `AbpLocalStorageService` that implements the `Storage` interface and works safely on both server and client:
```typescript
import { AbpLocalStorageService } from '@abp/ng.core';
@Injectable({ providedIn: 'root' })
export class MyService {
private storage = inject(AbpLocalStorageService);
saveData(key: string, value: string): void {
// Safe on both server and client
this.storage.setItem(key, value);
}
getData(key: string): string | null {
// Returns null on server, actual value on client
return this.storage.getItem(key);
}
}
```
`AbpLocalStorageService` implements all `Storage` methods:
- `getItem(key: string): string | null`
- `setItem(key: string, value: string): void`
- `removeItem(key: string): void`
- `clear(): void`
- `key(index: number): string | null`
- `length: number`
### 9.3. Hydration Mismatch Errors
If you see "NG0500" errors in the console:
1. Enable debug tracing (see section 6.1)
2. Check for dynamic content (dates, random IDs)
3. Ensure server and client render the same HTML
4. Use `TransferState` for data consistency
### 9.4. Avoiding Duplicate API Calls
ABP Core provides a `transferStateInterceptor` that automatically prevents duplicate HTTP GET requests during hydration. When you use `provideAbpCore()`, this interceptor is already active.
**How it works:**
- Server: Stores HTTP GET responses in `TransferState`
- Client: Reuses stored responses during hydration
- Automatically cleans up stored data after use
```typescript
// app.config.ts
import { provideAbpCore } from '@abp/ng.core';
export const appConfig: ApplicationConfig = {
providers: [
provideAbpCore(),
// transferStateInterceptor is automatically included
]
};
```
The interceptor works with all HTTP GET requests made through `HttpClient`:
```typescript
// This service automatically benefits from the interceptor
@Injectable({ providedIn: 'root' })
export class UserService {
private http = inject(HttpClient);
getUsers() {
// On server: Response is cached in TransferState
// On client: Cached response is used (no duplicate request)
return this.http.get<User[]>('/api/users');
}
}
```
> [!NOTE]
> The interceptor only works with GET requests. POST, PUT, DELETE, and PATCH requests are not cached.
## Additional Resources
- [Angular SSR Official Guide](https://angular.dev/guide/ssr)
- [Angular Hydration Documentation](https://angular.dev/guide/hydration)
- [PM2 Process Manager](https://pm2.keymetrics.io/)
## Summary
The ABP Angular SSR schematic provides:
- ✅ Automatic SSR setup with necessary dependencies
- ✅ Server-side authentication with OpenID Connect
- ✅ Multiple render modes (Server, Prerender, Client, Hybrid)
- ✅ Hydration support for better performance
Configure render modes based on your needs, handle platform differences properly, and use environment variables for deployment configuration.

126
docs/en/framework/ui/common/leptonx-css-variables.md

@ -0,0 +1,126 @@
```json
//[doc-seo]
{
"Description": "Explore LeptonX CSS Variables to customize theming with ease, controlling colors, spacing, and styles for a cohesive application design."
}
```
# LeptonX CSS Variables Documentation
LeptonX uses CSS custom properties (variables) prefixed with `--lpx-*` to provide a flexible theming system. These variables control colors, spacing, shadows, and component-specific styles throughout the application.
## Brand & Semantic Colors
| Variable | Description |
|----------|-------------|
| `--lpx-brand` | Brand-specific accent color |
| `--lpx-brand-text` | Text color used on brand-colored backgrounds |
## Base Colors
| Variable | Description |
|----------|-------------|
| `--lpx-light` | Light shade for subtle backgrounds or text |
| `--lpx-dark` | Dark shade for contrasting elements |
## Layout & Surface Colors
| Variable | Description |
|----------|-------------|
| `--lpx-content-bg` | Main content area background color |
| `--lpx-content-text` | Default text color for content areas |
| `--lpx-card-bg` | Card component background color |
| `--lpx-card-title-text-color` | Card title text color |
| `--lpx-border-color` | Default border color for dividers and outlines |
| `--lpx-shadow` | Box shadow definition for elevated elements |
## Navigation
| Variable | Description |
|----------|-------------|
| `--lpx-navbar-color` | Navbar background color |
| `--lpx-navbar-text-color` | Navbar default text/icon color |
| `--lpx-navbar-active-text-color` | Navbar active/hover text color |
| `--lpx-navbar-active-bg-color` | Navbar active item background color |
## Utility
| Variable | Description |
|----------|-------------|
| `--lpx-radius` | Global border-radius value for rounded corners |
## Global Override
Applies to all themes and pages:
```css
:root {
/* Brand & Semantic */
--lpx-brand: #f72585;
/* Base Colors */
--lpx-light: #f5f7fb;
--lpx-dark: #0b0f19;
/* Layout & Surface */
--lpx-content-bg: #101018;
--lpx-content-text: #cfd6e4;
--lpx-card-bg: #151a2b;
--lpx-card-title-text-color: #ffffff;
--lpx-border-color: #242836;
--lpx-shadow: 0 10px 30px rgba(0, 0, 0, 0.25);
/* Navigation */
--lpx-navbar-color: #0d1020;
--lpx-navbar-text-color: #aab2c8;
--lpx-navbar-active-text-color: #ffffff;
--lpx-navbar-active-bg-color: rgba(247, 37, 133, 0.15);
/* Utility */
--lpx-radius: 10px;
}
```
## Theme-Scoped Override
Applies only when a specific theme class is active (e.g., `.lpx-theme-dark` on `<html>` or `<body>`):
```css
:root .lpx-theme-dark {
/* Brand & Semantic */
--lpx-brand: #4dd0e1;
/* Base Colors */
--lpx-light: #e0f7fa;
--lpx-dark: #020617;
/* Layout & Surface */
--lpx-content-bg: #0b1118;
--lpx-content-text: #c7d0e0;
--lpx-card-bg: #111a24;
--lpx-card-title-text-color: #e6f1ff;
--lpx-border-color: #1e2a3a;
--lpx-shadow: 0 12px 32px rgba(0, 0, 0, 0.45);
/* Navigation */
--lpx-navbar-color: #0f1a22;
--lpx-navbar-text-color: #9fb3c8;
--lpx-navbar-active-text-color: #ffffff;
--lpx-navbar-active-bg-color: rgba(77, 208, 225, 0.18);
/* Utility */
--lpx-radius: 12px;
}
```
## Component/Page-Specific Override
For targeted customizations that should only affect a specific section:
```css
.my-custom-page {
--lpx-brand: #e91e63;
--lpx-card-bg: #1a1a2e;
}
```

BIN
docs/en/get-started/images/abp-studio-new-microservice-solution-dialog-optional-modules.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 24 KiB

BIN
docs/en/get-started/images/abp-studio-new-microservice-solution-dialog-properties.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 53 KiB

After

Width:  |  Height:  |  Size: 27 KiB

BIN
docs/en/get-started/images/abp-studio-new-solution-dialog-additional-options-microservice.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

After

Width:  |  Height:  |  Size: 34 KiB

BIN
docs/en/get-started/images/abp-studio-new-solution-dialog-additional-services.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 26 KiB

BIN
docs/en/get-started/images/abp-studio-new-solution-dialog-aspire-configuration-microservice.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

BIN
docs/en/get-started/images/abp-studio-new-solution-dialog-database-configurations-microservice.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 20 KiB

BIN
docs/en/get-started/images/abp-studio-new-solution-dialog-database-provider-microservice.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 24 KiB

BIN
docs/en/get-started/images/abp-studio-new-solution-dialog-dynamic-localization.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 30 KiB

BIN
docs/en/get-started/images/abp-studio-new-solution-dialog-languages-microservice.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

BIN
docs/en/get-started/images/abp-studio-new-solution-dialog-microservice.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 41 KiB

BIN
docs/en/get-started/images/abp-studio-new-solution-dialog-mobile-framework-microservice.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 30 KiB

BIN
docs/en/get-started/images/abp-studio-new-solution-dialog-multi-tenancy.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 28 KiB

BIN
docs/en/get-started/images/abp-studio-new-solution-dialog-public-web-site.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

After

Width:  |  Height:  |  Size: 25 KiB

BIN
docs/en/get-started/images/abp-studio-new-solution-dialog-ui-framework-microservice.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 90 KiB

After

Width:  |  Height:  |  Size: 51 KiB

BIN
docs/en/get-started/images/abp-studio-new-solution-dialog-ui-theme-microservice.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

After

Width:  |  Height:  |  Size: 28 KiB

19
docs/en/get-started/microservice.md

@ -94,6 +94,25 @@ Once you select the desired modules, click the *Next* button for the *UI Theme*
LeptonX is the suggested UI theme that is proper for production usage. Select one of the themes and configure the additional options if you want.
Click the Next button to see *Language Selection* selection:
![abp-studio-new-solution-dialog-languages](images/abp-studio-new-solution-dialog-languages-microservice.png)
In this step, you can choose which languages your application will support.
* Default Language: Select the main language for your app.
* Localizable User Interface: Turn this on to support multiple languages.
* Available Languages: Check the languages you want to include.
* Click Add Custom Language if you want to add a language that is not listed.
Click the Next button to see *.NET Aspire* configuration selection:
![abp-studio-new-solution-dialog-aspire-configuration](images/abp-studio-new-solution-dialog-aspire-configuration-microservice.png)
In this step, you can enable or disable the .NET Aspire integration for your solution. If you enable it, the solution will be pre-configured to work with .NET Aspire for easier microservice development and deployment. See the [Aspire Integration](../solution-templates/microservice/aspire-integration.md) document for more information about this feature.
Click the Next button to see *Additional Options* selection:

BIN
docs/en/images/add-passkey.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

BIN
docs/en/images/ai-management-widget.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

BIN
docs/en/images/my-passkey.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

BIN
docs/en/images/passkey-login.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

BIN
docs/en/images/passkey-login2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

BIN
docs/en/images/passkey-setting.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

BIN
docs/en/images/pen-test-alert-list-10.1.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

1
docs/en/modules/account-pro.md

@ -424,3 +424,4 @@ This module doesn't define any additional distributed event. See the [standard d
* [Linked Accounts](./account/linkedaccounts.md)
* [Session Management](./account/session-management.md)
* [Idle Session Timeout](./account/idle-session-timeout.md)
* [Web Authentication API (WebAuthn) passkeys](./account/passkey.md)

2
docs/en/modules/account/impersonation.md

@ -7,7 +7,7 @@
# Tenant impersonation & User impersonation
User impersonation allows you to temporarily sign in as a different user in your tenant's users. This article introduces how to enable impersonation in ABP. Impersonation is enabled by defautl in ABP v5.0 and above.
User impersonation allows you to temporarily sign in as a different user in your tenant's users. This article introduces how to enable impersonation in ABP. Impersonation is enabled by default in ABP v5.0 and above.
## Introduction

63
docs/en/modules/account/passkey.md

@ -0,0 +1,63 @@
# Web Authentication API (WebAuthn) passkeys
The `Web Authentication API (WebAuthn) passkeys` feature allows users to authenticate using passkeys, which are more secure and user-friendly alternatives to traditional passwords. Passkeys leverage public key cryptography to provide strong authentication without the need for users to remember complex passwords.
## Enabling Passkeys
You can enable/disable the `Web Authentication API (WebAuthn) passkeys` feature in the `Setting > Account > Passkeys` page. Also, there is an option to allow how many passkeys a user can register:
![passkey-setting](../../images/passkey-setting.png)
## Manage Passkeys
You can add/rename/delete your passkeys in the `Account/Manage` page:
![my-passkey](../../images/my-passkey.png)
Click the `Add Passkey` button to register a new passkey. You will be prompted to use your device's built-in biometric authentication (such as fingerprint or facial recognition) or an external security key to complete the registration process:
![add-passkey](../../images/add-passkey.png)
## Using Passkey for Login
Once you enable the passkey feature and register at least one passkey, you can use it to log in to your account. On the login page, select the `Passkey login` option and follow the prompts to authenticate using your registered passkey:
![passkey-login](../../images/passkey-login.png)
![passkey-login2](../../images/passkey-login2.png)
## Configure passkey options
ASP.NET Core Identity provides various options to configure passkey behavior through the `IdentityPasskeyOptions` class, which include:
- **AuthenticatorTimeout**: Gets or sets the time that the browser should wait for the authenticator to provide a passkey as a TimeSpan. This option applies to both creating a new passkey and requesting an existing passkey. This option is treated as a hint to the browser, and the browser may ignore the option. The default value is 5 minutes.
- **ChallengeSize**: Gets or sets the size of the challenge in bytes sent to the client during attestation and assertion. This option applies to both creating a new passkey and requesting an existing passkey. The default value is 32 bytes.
- **ServerDomain**: Gets or sets the effective Relying Party ID (domain) of the server. This should be unique and will be used as the identity for the server. This option applies to both creating a new passkey and requesting an existing passkey. If null, which is the default value, the server's origin is used. For more information, see Relying Party Identifier RP ID.
**Example configuration:**
```csharp
builder.Services.Configure<IdentityPasskeyOptions>(options =>
{
options.ServerDomain = "abp.io";
options.AuthenticatorTimeout = TimeSpan.FromMinutes(3);
options.ChallengeSize = 64;
});
```
For a complete list of configuration options, see [IdentityPasskeyOptions](https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.identity.identitypasskeyoptions). For the most up-to-date browser defaults, see the [W3C WebAuthn specification](https://www.w3.org/TR/webauthn-3/).
## HTTPS requirement
All passkey operations require HTTPS. The implementation stores authentication data in encrypted and signed cookies that could be intercepted over unencrypted connections.
## Browser Support
Passkeys are supported in most modern browsers, including: Chrome, Edge, Firefox, and Safari. Ensure that you are using the latest version of your browser to take advantage of passkey functionality.
## Additional resources
For more information on WebAuthn and passkeys, refer to the following resources:
- [Enable Web Authentication API (WebAuthn) passkeys](https://learn.microsoft.com/en-us/aspnet/core/security/authentication/passkeys)
- [Web Authentication API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Authentication_API)

253
docs/en/modules/ai-management/index.md

@ -28,7 +28,6 @@ abp add-module Volo.AIManagement
Open ABP Studio, navigate to your solution explorer, **Right Click** on the project and select **Import Module**. Choose `Volo.AIManagement` from `NuGet` tab and check the "Install this Module" checkbox. Click the "OK" button to install the module.
## Packages
This module follows the [module development best practices guide](../../framework/architecture/best-practices) and consists of several NuGet and NPM packages. See the guide if you want to understand the packages and relations between them.
@ -44,7 +43,7 @@ AI Management module packages are designed for various usage scenarios. Packages
AI Management module adds the following items to the "Main" menu:
* **AI Management**: Root menu item for AI Management module. (`AIManagement`)
* **Workspaces**: Workspace management page. (`AIManagement.Workspaces`)
* **Workspaces**: Workspace management page. (`AIManagement.Workspaces`)
`AIManagementMenus` class has the constants for the menu item names.
@ -87,21 +86,21 @@ Workspaces are the core concept of the AI Management module. A workspace represe
When creating or managing a workspace, you can configure the following properties:
| Property | Required | Description |
|----------|----------|-------------|
| `Name` | Yes | Unique workspace identifier (cannot contain spaces) |
| `Provider` | Yes* | AI provider name (e.g., "OpenAI", "Ollama") |
| `ModelName` | Yes* | Model identifier (e.g., "gpt-4", "mistral") |
| `ApiKey` | No | API authentication key (required by some providers) |
| `ApiBaseUrl` | No | Custom endpoint URL (defaults to provider's default) |
| `SystemPrompt` | No | Default system prompt for all conversations |
| `Temperature` | No | Response randomness (0.0-1.0, defaults to provider default) |
| `Description` | No | Workspace description |
| `IsActive` | No | Enable/disable the workspace (default: true) |
| `ApplicationName` | No | Associate workspace with specific application |
| `RequiredPermissionName` | No | Permission required to use this workspace |
| `IsSystem` | No | Whether it's a system workspace (read-only) |
| `OverrideSystemConfiguration` | No | Allow database configuration to override code-defined settings |
| Property | Required | Description |
| ----------------------------- | -------- | -------------------------------------------------------------- |
| `Name` | Yes | Unique workspace identifier (cannot contain spaces) |
| `Provider` | Yes* | AI provider name (e.g., "OpenAI", "Ollama") |
| `ModelName` | Yes* | Model identifier (e.g., "gpt-4", "mistral") |
| `ApiKey` | No | API authentication key (required by some providers) |
| `ApiBaseUrl` | No | Custom endpoint URL (defaults to provider's default) |
| `SystemPrompt` | No | Default system prompt for all conversations |
| `Temperature` | No | Response randomness (0.0-1.0, defaults to provider default) |
| `Description` | No | Workspace description |
| `IsActive` | No | Enable/disable the workspace (default: true) |
| `ApplicationName` | No | Associate workspace with specific application |
| `RequiredPermissionName` | No | Permission required to use this workspace |
| `IsSystem` | No | Whether it's a system workspace (read-only) |
| `OverrideSystemConfiguration` | No | Allow database configuration to override code-defined settings |
**\*Not required for system workspaces**
@ -165,7 +164,7 @@ public class WorkspaceDataSeederContributor : IDataSeedContributor, ITransientDe
workspace.ApiKey = "your-api-key";
workspace.SystemPrompt = "You are a helpful customer support assistant.";
await _workspaceRepository.InsertAsync(workspace);
}
```
@ -180,13 +179,12 @@ public class WorkspaceDataSeederContributor : IDataSeedContributor, ITransientDe
The AI Management module defines the following permissions:
| Permission | Description | Default Granted To |
|------------|-------------|-------------------|
| `AIManagement.Workspaces` | View workspaces | Admin role |
| `AIManagement.Workspaces.Create` | Create new workspaces | Admin role |
| `AIManagement.Workspaces.Update` | Edit existing workspaces | Admin role |
| `AIManagement.Workspaces.Delete` | Delete workspaces | Admin role |
| Permission | Description | Default Granted To |
| -------------------------------- | ------------------------ | ------------------ |
| `AIManagement.Workspaces` | View workspaces | Admin role |
| `AIManagement.Workspaces.Create` | Create new workspaces | Admin role |
| `AIManagement.Workspaces.Update` | Edit existing workspaces | Admin role |
| `AIManagement.Workspaces.Delete` | Delete workspaces | Admin role |
### Workspace-Level Permissions
@ -203,6 +201,7 @@ workspace.RequiredPermissionName = MyAppPermissions.AccessPremiumWorkspaces;
```
When a workspace has a required permission:
* Only authorized users with that permission can access the workspace endpoints
* Users without the permission will receive an authorization error
@ -220,6 +219,7 @@ The AI Management module is designed to support various usage patterns, from sim
In this scenario, you only use the ABP Framework's AI features directly. You configure AI providers (like OpenAI) in your code and don't need any database or management UI.
**Required Packages:**
- `Volo.Abp.AI`
- Any Microsoft AI extensions (e.g., `Microsoft.Extensions.AI.OpenAI`)
@ -257,7 +257,7 @@ public class MyService
{
_chatClient = chatClient;
}
public async Task<string> GetResponseAsync(string prompt)
{
var response = await _chatClient.CompleteAsync(prompt);
@ -277,10 +277,12 @@ In this scenario, you install the AI Management module with its database layer,
**Required Packages:**
**Minimum (backend only):**
- `Volo.AIManagement.EntityFrameworkCore` (or `Volo.AIManagement.MongoDB`)
- `Volo.AIManagement.OpenAI` (or another AI provider package)
**Full installation (with UI and API):**
- `Volo.AIManagement.EntityFrameworkCore` (or `Volo.AIManagement.MongoDB`)
- `Volo.AIManagement.Application`
- `Volo.AIManagement.HttpApi`
@ -315,6 +317,7 @@ public class YourModule : AbpModule
**Option 2 - Dynamic Workspace (UI-based):**
No code configuration needed. Define workspaces through:
- The AI Management UI (navigate to AI Management > Workspaces)
- Data seeding in your `DataSeeder` class
@ -339,6 +342,7 @@ public class MyService
In this scenario, your application communicates with a separate AI Management microservice that manages configurations and communicates with AI providers on your behalf. The AI Management service handles all AI provider interactions.
**Required Packages:**
- `Volo.AIManagement.Client.HttpApi.Client`
**Configuration:**
@ -399,7 +403,7 @@ public class MyService
var response = await _chatService.ChatCompletionsAsync(workspaceName, request);
return response.Content;
}
// For streaming responses
public async IAsyncEnumerable<string> StreamAIResponseAsync(string workspaceName, string prompt)
{
@ -426,6 +430,7 @@ public class MyService
This scenario builds on Scenario 3, but your application exposes its own HTTP endpoints that other applications can call. Your application then forwards these requests to the AI Management service.
**Required Packages:**
- `Volo.AIManagement.Client.HttpApi.Client` (to communicate with AI Management service)
- `Volo.AIManagement.Client.Application` (application services)
- `Volo.AIManagement.Client.HttpApi` (to expose HTTP endpoints)
@ -438,24 +443,170 @@ Same as Scenario 3, configure the remote AI Management service in `appsettings.j
**Usage:**
Once configured, other applications can call your application's endpoints:
- `POST /api/ai-management-client/chat-completion` for chat completions
- `POST /api/ai-management-client/stream-chat-completion` for streaming responses
Your application acts as a proxy, forwarding these requests to the AI Management microservice.
## Comparison Table
### Comparison Table
| Scenario | Database Required | Manages Config | Executes AI | Exposes API | Use Case |
| ------------------------- | ----------------- | -------------- | -------------- | ----------- | ----------------------------------------- |
| **1. No AI Management** | No | Code | Local | Optional | Simple apps, no config management needed |
| **2. Full AI Management** | Yes | Database/UI | Local | Optional | Monoliths, services managing their own AI |
| **3. Client Remote** | No | Remote Service | Remote Service | No | Microservices consuming AI centrally |
| **4. Client Proxy** | No | Remote Service | Remote Service | Yes | API Gateway pattern, proxy services |
## Client Usage (MVC UI)
AI Management uses different packages depending on the usage scenario:
- **`Volo.AIManagement.*` packages**: These contain the core AI functionality and are used when your application hosts and manages its own AI operations. These packages don't expose any application service and endpoints to be consumed by default.
- **`Volo.AIManagement.Client.*` packages**: These are designed for applications that need to consume AI services from a remote application. They provide both server and client side of remote access to the AI services.
**List of packages:**
- `Volo.AIManagement.Client.Application`
- `Volo.AIManagement.Client.Application.Contracts`
- `Volo.AIManagement.Client.HttpApi`
- `Volo.AIManagement.Client.HttpApi.Client`
- `Volo.AIManagement.Client.Web`
### The Chat Widget
The `Volo.AIManagement.Client.Web` package provides a chat widget to allow you to easily integrate a chat interface into your application that uses a specific AI workspace named `ChatClientChatViewComponent`.
#### Basic Usage
You can invoke the `ChatClientChatViewComponent` Widget in your razor page with the following code:
```csharp
@await Component.InvokeAsync(typeof(ChatClientChatViewComponent), new ChatClientChatViewModel
{
WorkspaceName = "mylama",
})
```
![ai-management-workspaces](../../images/ai-management-widget.png)
#### Properties
You can customize the chat widget with the following properties:
- `WorkspaceName`: The name of the workspace to use.
- `ComponentId`: Unique identifier for accessing the component via JavaScript API (stored in abp.chatComponents).
- `ConversationId`: The unique identifier for persisting and retrieving chat history from client-side storage.
- `Title`: The title of the chat widget.
- `ShowStreamCheckbox`: Whether to show the stream checkbox. Allows user to toggle streaming on and off. Default is `false`.
- `UseStreaming`: Default streaming behavior. Can be overridden by user when `ShowStreamCheckbox` is true.
```csharp
@await Component.InvokeAsync(typeof(ChatClientChatViewComponent), new ChatClientChatViewModel
{
WorkspaceName = "mylama",
ComponentId = "mylama-chat",
ConversationId = "mylama-conversation-" + @CurrentUser.Id,
Title = "My Custom Title",
ShowStreamCheckbox = true,
UseStreaming = true
})
```
#### Using the Conversation Id
You can use the `ConversationId` property to specify the id of the conversation to use. When the Conversation Id is provided, the chat will be stored at the client side and will be retrieved when the user revisits the page that contains the chat widget. If it's not provided or provided as **null**, the chat will be temporary and will not be saved, it'll be lost when the component lifetime ends.
```csharp
@await Component.InvokeAsync(typeof(ChatClientChatViewComponent), new ChatClientChatViewModel
{
WorkspaceName = "mylama",
ConversationId = "my-support-conversation-" + @CurrentUser.Id
})
```
#### JavaScript API
The chat components are initialized automatically when the ViewComponent is rendered in the page. All the initialized components in the page are stored in the `abp.chatComponents` object. You can retrieve a specific component by its `ComponentId` which is defined while invoking the ViewComponent.
```csharp
@await Component.InvokeAsync(typeof(ChatClientChatViewComponent), new ChatClientChatViewModel
{
WorkspaceName = "mylama",
ComponentId = "mylama-chat"
})
```
You can then use the JavaScript API to interact with the component.
```js
var chatComponent = abp.chatComponents.get('mylama-chat');
```
Once you have the component, you can use the following functions to interact with it:
```js
// Switch to a different conversation
chatComponent.switchConversation(conversationId);
// Create a new conversation with a specific model
chatComponent.createConversation(conversationId, modelName);
// Clear the current conversation history
chatComponent.clearConversation();
// Get the current conversation ID (returns null for ephemeral conversations)
var currentId = chatComponent.getCurrentConversationId();
// Initialize with a specific conversation ID
chatComponent.initialize(conversationId);
// Send a message programmatically
chatComponent.sendMessage();
// Listen to events
chatComponent.on('messageSent', function(data) {
console.log('Message sent:', data.message);
console.log('Conversation ID:', data.conversationId);
console.log('Is first message:', data.isFirstMessage);
});
chatComponent.on('messageReceived', function(data) {
console.log('AI response:', data.message);
console.log('Conversation ID:', data.conversationId);
console.log('Is streaming:', data.isStreaming);
});
chatComponent.on('streamStarted', function(data) {
console.log('Streaming started for conversation:', data.conversationId);
});
| Scenario | Database Required | Manages Config | Executes AI | Exposes API | Use Case |
|----------|------------------|----------------|-------------|-------------|----------|
| **1. No AI Management** | No | Code | Local | Optional | Simple apps, no config management needed |
| **2. Full AI Management** | Yes | Database/UI | Local | Optional | Monoliths, services managing their own AI |
| **3. Client Remote** | No | Remote Service | Remote Service | No | Microservices consuming AI centrally |
| **4. Client Proxy** | No | Remote Service | Remote Service | Yes | API Gateway pattern, proxy services |
// Remove event listeners
chatComponent.off('messageSent', callbackFunction);
```
**Best-practices:**
- Don't try to access the component at the page load time, it's not guaranteed to be initialized yet. Get the component whenever you need it to make sure it's **initialized** and the **latest state** is applied.
❌ Don't do this
```js
(function(){
var chatComponent = abp.chatComponents.get('mylama-chat');
$('#my-button').on('click', function() {
chatComponent.clearConversation();
});
});
```
✅ Do this
```js
(function(){
$('#my-button').on('click', function() {
var chatComponent = abp.chatComponents.get('mylama-chat');
chatComponent.clearConversation();
});
});
```
## Using Dynamic Workspace Configurations for custom requirements
The AI Management module allows you to access only configuration of a workspace without resolving pre-constructed chat client. This is useful when you want to use a workspace for your own purposes and you don't need to use the chat client.
The `IWorkspaceConfigurationStore` service is used to access the configuration of a workspace. It has multiple implementaations according to the usage scenario.
The `IWorkspaceConfigurationStore` service is used to access the configuration of a workspace. It has multiple implementations according to the usage scenario.
```csharp
public class MyService
@ -470,7 +621,7 @@ public class MyService
{
// Get the configuration of the workspace that can be managed dynamically.
var configuration = await _workspaceConfigurationStore.GetAsync("MyWorkspace");
// Do something with the configuration
var kernel = Kernel.CreateBuilder()
.AddAzureOpenAIChatClient(
@ -550,24 +701,23 @@ public override void ConfigureServices(ServiceConfigurationContext context)
> [!TIP]
> For production scenarios, you may want to add validation for the factory configuration.
### Available Configuration Properties
The `ChatClientCreationConfiguration` object provides the following properties from the database:
| Property | Type | Description |
|----------|------|-------------|
| `Name` | string | Workspace name |
| `Provider` | string | Provider name (e.g., "OpenAI", "Ollama") |
| `ApiKey` | string? | API key for authentication |
| `ModelName` | string | Model identifier (e.g., "gpt-4", "mistral") |
| `SystemPrompt` | string? | Default system prompt for the workspace |
| `Temperature` | float? | Temperature setting for response generation |
| `ApiBaseUrl` | string? | Custom API endpoint URL |
| `Description` | string? | Workspace description |
| `IsActive` | bool | Whether the workspace is active |
| `IsSystem` | bool | Whether it's a system workspace |
| `RequiredPermissionName` | string? | Permission required to use this workspace |
| Property | Type | Description |
| ------------------------ | ------- | ------------------------------------------- |
| `Name` | string | Workspace name |
| `Provider` | string | Provider name (e.g., "OpenAI", "Ollama") |
| `ApiKey` | string? | API key for authentication |
| `ModelName` | string | Model identifier (e.g., "gpt-4", "mistral") |
| `SystemPrompt` | string? | Default system prompt for the workspace |
| `Temperature` | float? | Temperature setting for response generation |
| `ApiBaseUrl` | string? | Custom API endpoint URL |
| `Description` | string? | Workspace description |
| `IsActive` | bool | Whether the workspace is active |
| `IsSystem` | bool | Whether it's a system workspace |
| `RequiredPermissionName` | string? | Permission required to use this workspace |
### Example: Azure OpenAI Factory
@ -604,6 +754,7 @@ public class AzureOpenAIChatClientFactory : IChatClientFactory, ITransientDepend
After implementing and registering your factory:
1. **Through UI**: Navigate to the AI Management workspaces page and create a new workspace:
- Select your provider name (e.g., "Ollama", "AzureOpenAI")
- Configure the API settings
- Set the model name
@ -672,6 +823,7 @@ WorkspaceConfiguration:{ApplicationName}:{WorkspaceName}
```
### HttpApi Client Layer
- `IntegrationWorkspaceConfigurationStore`: Integration service for remote workspace configuration retrieval. Implements `IWorkspaceConfigurationStore` interface.
The cache is automatically invalidated when workspaces are created, updated, or deleted.
@ -680,4 +832,5 @@ The cache is automatically invalidated when workspaces are created, updated, or
- [Artificial Intelligence Infrastructure](../../framework/infrastructure/artificial-intelligence/index.md): Learn about the underlying AI workspace infrastructure
- [Microsoft.Extensions.AI](https://learn.microsoft.com/en-us/dotnet/ai/): Microsoft's unified AI abstractions
- [Microsoft Agent Framework](https://learn.microsoft.com/en-us/agent-framework/overview/agent-framework-overview): Microsoft's Agent Framework
- [Semantic Kernel](https://learn.microsoft.com/en-us/semantic-kernel/): Microsoft's Semantic Kernel integration

3
docs/en/modules/feature-management.md

@ -72,9 +72,10 @@ namespace Demo
## Feature Management Providers
Features Management Module is extensible, just like the [features system](../framework/infrastructure/features.md). You can extend it by defining feature management providers. There are 3 pre-built feature management providers registered it the following order:
Features Management Module is extensible, just like the [features system](../framework/infrastructure/features.md). You can extend it by defining feature management providers. There are 4 pre-built feature management providers registered in the following order:
* `DefaultValueFeatureManagementProvider`: Gets the value from the default value of the feature definition. It can not set the default value since default values are hard-coded on the feature definition.
* `ConfigurationFeatureManagementProvider`: Gets the value from the [IConfiguration service](../framework/fundamentals/configuration.md).
* `EditionFeatureManagementProvider`: Gets or sets the feature values for an edition. Edition is a group of features assigned to tenants. Edition system has not implemented by the Tenant Management module. You can implement it yourself or purchase the ABP [SaaS Module](https://abp.io/modules/Volo.Saas) which implements it and also provides more SaaS features, like subscription and payment.
* `TenantFeatureManagementProvider`: Gets or sets the features values for tenants.

2
docs/en/modules/saas.md

@ -40,7 +40,7 @@ Firstly, Payment module must be configured properly:
```bash
abp add-module Volo.Payment
```
_Or you can install via using ABP Suite._
_Or you can install via using ABP Studio._
- Configure Saas module to use Payment.

88
docs/en/others/penetration-test-report.md

@ -7,7 +7,7 @@
# ABP Penetration Test Report
The ABP Commercial MVC `v9.1.0` application template has been tested against security vulnerabilities by the [OWASP ZAP v2.14.0](https://www.zaproxy.org/) tool. The demo web application was started on the `https://localhost:44349` address. The below alerts have been reported by the pentest tool. These alerts are sorted by the risk level as high, medium, and low. The informational alerts are not mentioned in this document.
The ABP Commercial MVC `v10.0.1` application template has been tested against security vulnerabilities by the [OWASP ZAP v2.14.0](https://www.zaproxy.org/) tool. The demo web application was started on the `https://localhost:44349` address. The below alerts have been reported by the pentest tool. These alerts are sorted by the risk level as high, medium, and low. The informational alerts are not mentioned in this document.
Many of these alerts are **false-positive**, meaning the vulnerability scanner detected these issues, but they are not exploitable. It's clearly explained for each false-positive alert why this alert is a false-positive.
@ -17,53 +17,75 @@ In the next sections, you will find the affected URLs, attack parameters (reques
There are high _(red flag)_, medium _(orange flag)_, low _(yellow flag)_, and informational _(blue flag)_ alerts.
![penetration-test-9.1.0](../images/pen-test-alert-list-9.1.png)
![penetration-test-10.0.1](../images/pen-test-alert-list-10.1.png)
> The informational alerts are not mentioned in this document. These alerts are not raising any risks on your application and they are optional.
> The informational alerts are not mentioned in this document. These alerts don't raise any risks for your application and they are optional.
### Spring4Shell [Risk: High] - False Positive
### Cross Site Scripting (Reflected) [Risk: High] - Positive
- *[POST] - https://localhost:44349/Account/ForgotPassword* (attack: **class.module.classLoader.DefaultAssertionStatus=nonsense**)
- *[POST] - https://localhost:44349/Account/Login* (attack: **class.module.classLoader.DefaultAssertionStatus=nonsense**)
- *[POST] - https://localhost:44349/Account/Login?ReturnUrl=%2FSettingManagement* (attack: **class.module.classLoader.DefaultAssertionStatus=nonsense**)
- *[GET] - https://localhost:44349/Identity/OrganizationUnits/AddMemberModal?title=SelectAUser&organizationUnitId=...&OrganizationUnitName=%3C%2Fh5%3E%3CscrIpt%3Ealert%281%29%3B%3C%2FscRipt%3E%3Ch5%3E*
- *[GET] - https://localhost:44349/Identity/OrganizationUnits/AddRoleModal?organizationUnitId=...&OrganizationUnitName=%3C%2Fh5%3E%3CscrIpt%3Ealert%281%29%3B%3C%2FscRipt%3E%3Ch5%3E*
- *[GET] - https://localhost:44349/Saas/Host/Tenants/ImpersonateTenantModal?tenantId=...&tenantName=%3C%2Fh5%3E%3CscrIpt%3Ealert%281%29%3B%3C%2FscRipt%3E%3Ch5%3E*
**Description**:
The application appears to be vulnerable to CVE-2022-22965 (otherwise known as Spring4Shell) - remote code execution (RCE) via data binding.
Cross-site Scripting (XSS) is an attack technique that involves echoing attacker-supplied code into a user's browser instance.
**Explanation**:
ABP Framework is built on top of ASP.NET Core and does not use the Spring Framework. This application does not rely on Java-based technologies, making it immune to vulnerabilities like Spring4Shell. The detection is a false positive as there are no Spring dependencies in the project.
This is a **Positive** alert. The application reflects the `OrganizationUnitName` and `tenantName` parameters without proper encoding in the modal headers, allowing for the execution of arbitrary JavaScript. We have created an **internal issue** to track this vulnerability, and it will be fixed in the next release.
### Absence of Anti-CSRF Tokens [Risk: Medium] — False Positive
### PII Disclosure [Risk: High] - False Positive
* *[GET] - https://localhost:44349/Account/LinkUsers/LinkUsersModal?returnUrl=/SettingManagement*
* *[GET] — https://localhost:44349/Account/Manage* (same URL with different query parameters)
* *[GET] - https://localhost:44349/HostDashboard*
* *[GET] - https://localhost:44349/SettingManagement?handler=RenderView&id=Volo.Abp.Account* (other several URLs)
- *[GET] - https://localhost:44349/* (Evidence: 639002492030480000)
- *[GET] - https://localhost:44349/?page=...*
**Description**:
**Description**:
No Anti-CSRF tokens were found in an HTML submission form.
A cross-site request forgery is an attack that involves forcing a victim to send an HTTP request to a target destination without their knowledge or intent in order to perform an action as the victim. The underlying cause is application functionality using predictable URL/form actions in a repeatable way. The nature of the attack is that CSRF exploits the trust that a website has for a user. By contrast, cross-site scripting (XSS) exploits the trust that a user has in a website. Like XSS, CSRF attacks are not necessarily cross-site, but they can be. Cross-site request forgery is also known as CSRF, XSRF, one-click attack, session riding, confused deputy, and sea surf.
The response contains Personally Identifiable Information, such as CC number, SSN and similar sensitive data.
**Explanation:**
**Explanation**:
This is a **false-positive** alert because ABP provides the Anti-CSRF token via a cookie as seen on the following screenshot:
This is a **false-positive** alert. The detected numbers (e.g., `639002492030480000`) are cache-busting timestamps (`_v` parameter) generated by the framework for static assets. They coincidentally match the pattern of Credit Card numbers (pattern matching) but are not sensitive data.
![Absence of Anti-CSRF Token](../images/pen-test-alert-remote-os-command-injection.png)
### Path Traversal [Risk: High] - False Positive
### Application Error Disclosure [Risk: Medium] - False Positive
- *[GET] - https://localhost:44349/Account/Login?returnUrl=Login*
- *[GET] - https://localhost:44349/api/account/security-logs?action=\security-logs*
- *[GET] — https://localhost:44349/AuditLogs*
**Description**:
**Description**:
The Path Traversal attack technique allows an attacker access to files, directories, and commands that potentially reside outside the web document root directory.
**Explanation**:
This is a **false-positive** alert. ABP Framework automatically validates `returnUrl` parameters and ensures they are local to the application or within a whitelist. The application does not return file contents based on these parameters.
### SQL Injection [Risk: High] - False Positive
- *[GET] - https://localhost:44349/AbpPermissionManagement/PermissionManagementModal?providerKey=AbpSolution16711_Swagger+AND+1%3D1+--+*
- *[GET] - https://localhost:44349/Account/Manage?CurrentPassword=ZAP%27+AND+%271%27%3D%271%27+--+*
**Description**:
SQL injection may be possible.
This page contains an error/warning message that may disclose sensitive information like the location of the file that produced the unhandled exception. This information can be used to launch further attacks against the web application. The alert could be a false positive if the error message is found inside a documentation page.
**Explanation**:
This is a **false-positive** alert. ABP Framework uses Entity Framework Core, which inherently uses parameterized queries, preventing standard SQL injection attacks. Manual verification showed that injecting SQL syntax into parameters like `providerKey` results in the input being treated as a literal string (resulting in no match or default behavior) rather than altering the query structure.
### SQL Injection - SQLite [Risk: High] - False Positive
- *[POST] - https://localhost:44349/Account/ForgotPassword?returnUrl=%2FAccount%2FManage* (Attack: `case randomblob(100000) ...`)
- *[POST] - https://localhost:44349/FeatureManagement/FeatureManagementModal*
**Description**:
SQL injection may be possible.
**Explanation**:
There are only one URL that is reported as exposing error messages. This is a **false-positive** alert. The [Audit Logging Module](../modules/audit-logging.md), shows request & response details and exception information, these are not sensitive information and only can be seen by the users whose related permissions are granted.
This is a **false-positive** alert. Similar to the standard SQL Injection alert, the application uses parameterized queries. The detected delays are likely due to application processing variations or network latency rather than successful SQL injection.
### Content Security Policy (CSP) Header Not Set [Risk: Medium] — Positive (Fixed)
@ -238,23 +260,7 @@ A cookie has been set with its `SameSite` attribute set to `none`, which means t
Ensure that the `SameSite` attribute is set to either `lax` or ideally `strict` for all cookies. We discussed setting the **SameSite** attribute to `strict` in the following issue [github.com/abpframework/abp/issues/14215](https://github.com/abpframework/abp/issues/14215) and decided to leave this change to the final developer.
### Information Disclosure - Debug Error Messages [Risk: Low] — False Positive
* *[GET] - https://localhost:44349/AuditLogs*
**Description:**
The response appeared to contain common error messages returned by platforms such as ASP.NET, and Web-servers such as IIS and Apache. You can configure the list of common debug messages.
**Solution:**
Disable debugging messages before pushing them to production.
**Explanation:**
The response of the endpoints above return localization texts which are not real error messages. As there is no real error in the backend side, this vulnerability is a **false-positive** alert.
![Information Disclosure - Debug Error Messages](../images/pen-test-information-disclosure.png)
### Strict-Transport-Security Header Not Set [Risk: Low] - False Positive

372
docs/en/solution-templates/microservice/aspire-integration.md

@ -0,0 +1,372 @@
```json
//[doc-seo]
{
"Description": "Learn how to integrate .NET Aspire into your ABP-based solution for streamlined development, orchestration, and observability of distributed applications."
}
```
# .NET Aspire Integration
````json
//[doc-nav]
{
"Next": {
"Name": "Miscellaneous guides in the Microservice solution",
"Path": "solution-templates/microservice/guides"
}
}
````
> You must have an ABP Business or a higher license to be able to create a microservice solution.
## .NET Aspire Overview
[Aspire](https://aspire.dev/get-started/what-is-aspire/) streamlines building, running, debugging, and deploying distributed apps. Picture your app as a set of services, databases, and frontends—when they’re deployed, they all work together seamlessly, but every time you develop them they need to be individually started and connected. With Aspire, you get a unified toolchain that eliminates complex configs and makes local debugging effortless. Instantly launch and debug your entire app with a single command. Ready to deploy? Aspire lets you publish anywhere—Kubernetes, the cloud, or your own servers. It’s also fully extensible, so you can integrate your favorite tools and services with ease. It provides:
- **Orchestration**: A code-first approach to defining and running distributed applications, managing dependencies, and launch order.
- **Integrations**: Pre-built components for common services (databases, caches, message brokers) with automatic configuration.
- **Tooling**: A developer dashboard for real-time monitoring of logs, traces, metrics, and resource health.
- **Service Discovery**: Automatic service-to-service communication without hardcoded endpoints.
- **Observability**: Built-in OpenTelemetry support for distributed tracing, metrics, and structured logging.
## ABP Integration
When you enable .NET Aspire in an ABP microservice solution, you get a fully integrated development experience where:
- All microservices, gateways, and applications are orchestrated through a single entry point (AppHost).
- Infrastructure containers (databases, Redis, RabbitMQ, Elasticsearch, etc.) are managed as code.
- OpenTelemetry, health checks, and service discovery are automatically configured for all projects via the shared ServiceDefaults project.
## Enabling Aspire
When creating a new microservice solution via ABP Studio:
1. In the solution creation wizard, look for the **".NET Aspire Integration"** step.
2. Toggle the option to **enable .NET Aspire**.
3. Complete the wizard—Aspire projects will be generated along with your solution.
![Enable Aspire in ABP Studio](images/aspire-configuration.png)
## Solution Structure Changes
When Aspire is enabled, two additional projects are added to your solution:
![Aspire Solution Structure](images/aspire-solution-structure.png)
### AppHost (Orchestrator)
[`AppHost`](https://aspire.dev/get-started/app-host/) is the .NET Aspire orchestrator project that declares all resources (services, databases, containers, applications) and their dependencies in C# code. All services, gateways, and applications in the solution have their project references added to `AppHost`.
**Why is it added?**
- Centralized orchestration: Start your entire microservice ecosystem with a single command.
- Code-first infrastructure: Databases, Redis, RabbitMQ, Elasticsearch, and observability tools are defined programmatically.
- Dependency management: `AppHost` ensures services start in the correct order using `WaitFor()` declarations.
- Automatic configuration: Connection strings, endpoints, and environment variables are injected automatically.
**Key files in AppHost:**
| File | Purpose |
|--------|------------|
| `AppHost.cs` | Entry point—creates the distributed application builder and adds all resources |
| `AppHostExtensions.cs` | Extension methods for adding infrastructures, databases, microservices, gateways, and applications |
**What it manages:**
- Database servers (SQL Server, PostgreSQL, MySQL, or MongoDB)
- Per-service databases
- Redis (caching)
- RabbitMQ (messaging)
- Elasticsearch + Kibana (logging)
- Prometheus + Grafana (metrics)
- Jaeger (tracing)
- OpenTelemetry Collector
- All microservices, gateways, and web applications
![Aspire AppHost Resource Topology](images/aspire-apphost-topology.png)
### ServiceDefaults
[`ServiceDefaults`](https://aspire.dev/fundamentals/service-defaults/) is a shared library that provides common cloud-native configuration for all projects in the solution. The `ServiceDefaults` project reference is added to all services, gateways, and applications in the solution.
**Why is it added?**
- Consistency: Every service uses the same observability, health check, and resilience patterns.
- Less boilerplate: Add `builder.AddServiceDefaults()` once, get all defaults automatically.
- Production-ready: OpenTelemetry, health endpoints, and HTTP resilience are preconfigured.
**What it provides:**
| Feature | Description |
|---------|-------------|
| OpenTelemetry | Tracing, metrics, and structured logging with automatic instrumentation |
| Health Checks | `/health` and `/alive` endpoints for Kubernetes-style probes |
| Service Discovery | Automatic resolution of service endpoints |
| HTTP Resilience | Retry policies, timeouts, and circuit breakers for HTTP clients |
**Usage example:**
```csharp
var builder = WebApplication.CreateBuilder(args);
builder.AddServiceDefaults(); // Adds all cloud-native defaults
// ... rest of configuration
```
## Running the Solution
### Without Aspire
1. Open **Solution Runner** in ABP Studio.
2. Start all resources in Solution Runner (services, gateways, applications, and tools such as databases, Redis, RabbitMQ, etc.) individually or collectively using the `Default` profile.
![Solution Runner without Aspire](images/solution-runner-default-profile.png)
### With Aspire
1. Open **Solution Runner** in ABP Studio.
2. Select the **Aspire** profile.
3. Run `MyCompanyName.MyProjectName.AppHost`.
4. AppHost automatically:
- Starts all infrastructure containers (database, Redis, RabbitMQ, Elasticsearch, etc.).
- Launches all microservices, gateways, and applications in dependency order.
- Injects connection strings and environment variables.
- Opens the Aspire Dashboard for monitoring.
![Solution Runner with Aspire](images/solution-runner-aspire-profile.png)
## Aspire Dashboard
The dashboard enables real-time tracking of key aspects of your app, including logs, traces, and environment configurations. It's designed to enhance the development experience by providing a clear and insightful view of your app's state and structure.
Key features of the dashboard include:
- Real-time tracking of logs, traces, and environment configurations.
- User interface to stop, start, and restart resources.
- Collects and displays logs and telemetry; view structured logs, traces, and metrics in an intuitive UI.
- Enhanced debugging with GitHub Copilot, your AI-powered assistant built into the dashboard.
### Opening the Dashboard
When AppHost starts, the Aspire Dashboard opens automatically in ABP Studio's built-in browser at `https://localhost:15105`. Alternatively, you can right-click on AppHost in Solution Runner and select **Browse** to open it manually.
![Aspire Dashboard Login](images/aspire-dashboard-login.png)
**Dashboard login token**: `1q2w3E*` (default, configurable via launch settings)
### Dashboard Features
The dashboard includes several tabs, each offering different insights into your application:
#### Resources
View the status of all resources in your application, including projects, containers, and executables. Monitor health checks, view environment variables, and access endpoints for each resource.
![Aspire Dashboard Resources Tab](images/aspire-dashboard-resources.png)
#### Console
Display console logs from all resources in real-time. Filter by resource and log level to quickly find relevant information during development and debugging.
![Aspire Dashboard Console Tab](images/aspire-dashboard-console.png)
#### Structured Logs
View structured logs from all resources with advanced filtering capabilities. Search and filter logs by resource, log level, timestamp, and custom properties to diagnose issues efficiently.
![Aspire Dashboard Structured Logs Tab](images/aspire-dashboard-structured-logs.png)
#### Traces
Explore distributed traces across your microservices to understand request flows and identify performance bottlenecks. Visualize how requests propagate through different services and examine timing information.
![Aspire Dashboard Traces Tab](images/aspire-dashboard-traces.png)
#### Metrics
Monitor real-time metrics including HTTP requests, response times, garbage collection, memory usage, and custom metrics. Visualize metric trends with interactive charts to understand application performance.
![Aspire Dashboard Metrics Tab](images/aspire-dashboard-metrics.png)
## Tools and Their Management UIs
AppHost pre-configures the following observability and tools. The URLs below are for their **management/dashboard interfaces** (these tools may expose additional internal endpoints for service communication).
All URLs and configurations are defined in the `AppHost` project. If you need to change ports or other settings, you can modify them in the `AppHost` project.
After running AppHost, you can access these tools either by opening the URLs directly in your browser or via Solution Runner **Tools** tab.
### Database Management System Admin Tool
The database management admin tool varies by database type:
| Database | Tool | URL |
|----------|------|-----|
| SQL Server | DBeaver CloudBeaver | `http://localhost:8081` |
| MySQL | phpMyAdmin | `http://localhost:8082` |
| PostgreSQL | pgAdmin | `http://localhost:8083` |
| MongoDB | Mongo Express | `http://localhost:8084` |
For example, if using PostgreSQL, access **pgAdmin** at `http://localhost:8083` or via Solution Runner Tools tab:
![pgAdmin Dashboard](images/aspire-database-postgre-pgadmin.png)
### Grafana
**URL**: `http://localhost:3001`
**Credentials**: `admin` / `admin`
Grafana is a visualization and analytics platform for monitoring metrics. It provides interactive dashboards with charts and graphs for tracking application performance.
![Grafana Dashboard](images/aspire-grafana-dashboard.png)
### Jaeger
**URL**: `http://localhost:16686`
**Credentials**: No authentication required
Jaeger is a distributed tracing system to monitor and troubleshoot problems on interconnected software components called microservices.
![Jaeger Traces](images/aspire-jaeger-traces.png)
### Kibana
**URL**: `http://localhost:5601`
**Credentials**: No authentication required
Kibana is a visualization tool for Elasticsearch data. It provides search and data visualization capabilities for logs stored in Elasticsearch.
![Kibana Dashboard](images/aspire-kibana-dashboard.png)
### Prometheus
**URL**: `http://localhost:9090`
**Credentials**: No authentication required
Prometheus is a monitoring and alerting toolkit. It collects and stores metrics as time series data, allowing you to query and analyze application performance.
![Prometheus Dashboard](images/aspire-prometheus-dashboard.png)
### RabbitMQ Management
**URL**: `http://localhost:15672`
**Credentials**: `guest` / `guest`
RabbitMQ Management UI provides a web-based interface for managing and monitoring RabbitMQ message broker, including queues, exchanges, and message flows.
![RabbitMQ Management](images/aspire-rabbitmq-management.png)
### Redis Insight
**URL**: `http://localhost:5540`
**Credentials**: No authentication required
Redis Insight is a visual tool for Redis that allows you to browse data, run commands, and monitor Redis performance.
![Redis Insight](images/aspire-redis-insight.png)
## Adding New Services, Gateways, or Applications
When you add a new microservice, gateway, or application via **ABP Studio**:
1. `AppHost` is updated automatically - the new project is registered as a resource with appropriate configurations.
2. `ServiceDefaults` is referenced - the new project gets cloud-native defaults.
> You don't need to manually edit `AppHost` in most cases.
## Adding a Resource Manually
If you need to add a resource manually (not via **ABP Studio**), follow these steps:
### 1. Reference ServiceDefaults in your new project
```xml
<ProjectReference Include="..\..\..\aspire\service-defaults\MyCompanyName.MyProjectName.ServiceDefaults\MyCompanyName.MyProjectName.ServiceDefaults.csproj" />
```
> Adjust the path as necessary based on your solution structure.
### 2. Add ServiceDefaults in Program.cs
```csharp
var builder = WebApplication.CreateBuilder(args);
builder.AddServiceDefaults();
// ... your configuration
```
### 3. Add Project Reference to AppHost
Add a reference to your resource project in `MySolutionName.MyProjectName.AppHost/MySolutionName.MyProjectName.AppHost.csproj`:
```xml
<ProjectReference Include="..\..\services\myresource\src\MySolutionName.MyProjectName.MyResource\MySolutionName.MyProjectName.MyResource.csproj" />
```
### 4. Register Resource in AppHost
Edit `AppHostExtensions.cs` and add your resource in the `AddAdditionalResources` method:
```csharp
var myResource = builder
.AddProject<Projects.MySolutionName_MyProjectName_ServiceName>("myresource", "MySolutionName.MyProjectName.MyResource")
.WaitFor(databases.AdministrationDb)
.WaitFor(databases.IdentityDb)
.WaitFor(databases.MyResourceDb)
.WaitFor(databases.AuditLoggingDb)
.WaitFor(databases.SaasDb)
.WaitFor(databases.LanguageManagementDb)
.WaitFor(redis)
.WaitFor(rabbitMq)
.WithReference(databases.AdministrationDb)
.WithReference(databases.IdentityDb)
.WithReference(databases.BlobStoringDb)
.WithReference(databases.MyResourceDb)
.WithReference(databases.AuditLoggingDb)
.WithReference(databases.SaasDb)
.WithReference(databases.LanguageManagementDb)
.ConfigureRabbitMq(rabbitMq, infrastructureDefaultUser, infrastructureDefaultUserPassword)
.ConfigureRedis(redis)
.ConfigureElasticSearch(elasticsearch);
applicationResources["MyResource"] = myResource;
```
> Adjust the dependencies and configurations as necessary.
### 5. Configure Gateway (if needed)
If your resource should be accessible through a gateway, add the gateway configuration in the `AddAdditionalResources` method:
```csharp
var webgateway = applicationResources.FirstOrDefault(x => x.Key == "WebGateway").Value;
if (webgateway != null)
{
webgateway
.WaitFor(applicationResources["MyResource"])
.WithReference(applicationResources["MyResource"])
.WithEnvironment("ReverseProxy__Clusters__MyResource__Destinations__MyResource__Address", "http://MyResource");
}
```
### 6. Configure Authentication Server (if needed)
If your resource needs to be added to `CORS` and `RedirectAllowedUrls` configuration for the authentication server, update the `allowedUrls` variable in the `ConfigureAuthServer` method:
```csharp
var allowedUrls = ReferenceExpression.Create($"{applicationResources["MyResource"].GetEndpoint("http")},...");
```
### 7. Add Database (if needed)
If your resource requires a dedicated database, add it in the `AddDatabases` method:
```csharp
var myResourceDb = databaseServers.Postgres.AddDatabase("MyResource", "MySolutionName.MyProjectName_MyResource");
```
> Adjust the database management system as necessary.
### 8. Add to Solution Runner Profiles (optional)
To run your resource in **Solution Runner** profiles(Default or Aspire), add it following the instructions in the [Studio running applications documentation](../../studio/running-applications.md#add).
## Deploying the Application
.NET Aspire supports deployment to Azure Container Apps, Kubernetes, and other cloud platforms. For detailed deployment guidance, see the official documentation: [.NET Aspire Deployment](https://aspire.dev/deployment/overview/)
To learn more about .NET Aspire, visit: https://aspire.dev/get-started/what-is-aspire/

4
docs/en/solution-templates/microservice/helm-charts-and-kubernetes.md

@ -11,8 +11,8 @@
//[doc-nav]
{
"Next": {
"Name": "Miscellaneous guides in the Microservice solution",
"Path": "solution-templates/microservice/guides"
"Name": "Aspire Integration in the Microservice solution",
"Path": "solution-templates/microservice/aspire-integration"
}
}
````

BIN
docs/en/solution-templates/microservice/images/aspire-apphost-topology.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 246 KiB

BIN
docs/en/solution-templates/microservice/images/aspire-configuration.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

BIN
docs/en/solution-templates/microservice/images/aspire-dashboard-console.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 541 KiB

BIN
docs/en/solution-templates/microservice/images/aspire-dashboard-login.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 180 KiB

BIN
docs/en/solution-templates/microservice/images/aspire-dashboard-metrics.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 197 KiB

BIN
docs/en/solution-templates/microservice/images/aspire-dashboard-resources.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 238 KiB

BIN
docs/en/solution-templates/microservice/images/aspire-dashboard-structured-logs.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 232 KiB

BIN
docs/en/solution-templates/microservice/images/aspire-dashboard-traces.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 186 KiB

BIN
docs/en/solution-templates/microservice/images/aspire-database-postgre-pgadmin.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 262 KiB

BIN
docs/en/solution-templates/microservice/images/aspire-grafana-dashboard.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 376 KiB

BIN
docs/en/solution-templates/microservice/images/aspire-jaeger-traces.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 262 KiB

BIN
docs/en/solution-templates/microservice/images/aspire-kibana-dashboard.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 442 KiB

BIN
docs/en/solution-templates/microservice/images/aspire-prometheus-dashboard.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 440 KiB

BIN
docs/en/solution-templates/microservice/images/aspire-rabbitmq-management.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 249 KiB

BIN
docs/en/solution-templates/microservice/images/aspire-redis-insight.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 384 KiB

BIN
docs/en/solution-templates/microservice/images/aspire-solution-structure.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

BIN
docs/en/solution-templates/microservice/images/solution-runner-aspire-profile.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

BIN
docs/en/solution-templates/microservice/images/solution-runner-default-profile.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save