diff --git a/AppHost/AppHost.csproj b/AppHost/AppHost.csproj
new file mode 100644
index 0000000..53c3824
--- /dev/null
+++ b/AppHost/AppHost.csproj
@@ -0,0 +1,20 @@
+
+
+
+ Exe
+ net8.0
+ enable
+ enable
+ true
+ 045801e7-58a1-4ee6-9d28-93c23b5dcd6b
+
+
+
+
+
+
+
+
+
+
+
diff --git a/AppHost/Program.cs b/AppHost/Program.cs
new file mode 100644
index 0000000..c62c3a0
--- /dev/null
+++ b/AppHost/Program.cs
@@ -0,0 +1,3 @@
+var builder = DistributedApplication.CreateBuilder(args);
+
+builder.Build().Run();
diff --git a/AppHost/Properties/launchSettings.json b/AppHost/Properties/launchSettings.json
new file mode 100644
index 0000000..c69c691
--- /dev/null
+++ b/AppHost/Properties/launchSettings.json
@@ -0,0 +1,29 @@
+{
+ "$schema": "https://json.schemastore.org/launchsettings.json",
+ "profiles": {
+ "https": {
+ "commandName": "Project",
+ "dotnetRunMessages": true,
+ "launchBrowser": true,
+ "applicationUrl": "https://localhost:17057;http://localhost:15006",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development",
+ "DOTNET_ENVIRONMENT": "Development",
+ "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:21274",
+ "DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:22232"
+ }
+ },
+ "http": {
+ "commandName": "Project",
+ "dotnetRunMessages": true,
+ "launchBrowser": true,
+ "applicationUrl": "http://localhost:15006",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development",
+ "DOTNET_ENVIRONMENT": "Development",
+ "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "http://localhost:19034",
+ "DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "http://localhost:20044"
+ }
+ }
+ }
+}
diff --git a/AppHost/appsettings.Development.json b/AppHost/appsettings.Development.json
new file mode 100644
index 0000000..0c208ae
--- /dev/null
+++ b/AppHost/appsettings.Development.json
@@ -0,0 +1,8 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft.AspNetCore": "Warning"
+ }
+ }
+}
diff --git a/AppHost/appsettings.json b/AppHost/appsettings.json
new file mode 100644
index 0000000..31c092a
--- /dev/null
+++ b/AppHost/appsettings.json
@@ -0,0 +1,9 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft.AspNetCore": "Warning",
+ "Aspire.Hosting.Dcp": "Warning"
+ }
+ }
+}
diff --git a/Netina.AdminPanel.PWA/Dialogs/DiscountActionDialogBox.razor.cs b/Netina.AdminPanel.PWA/Dialogs/DiscountActionDialogBox.razor.cs
index 7515bd7..5c4444e 100644
--- a/Netina.AdminPanel.PWA/Dialogs/DiscountActionDialogBox.razor.cs
+++ b/Netina.AdminPanel.PWA/Dialogs/DiscountActionDialogBox.razor.cs
@@ -194,16 +194,42 @@ public class DiscountActionDialogBoxViewModel : BaseViewModel
if(StartDate != null)
PageDto.StartDate = StartDate.Value;
- var request = PageDto.Adapt();
- await _restWrapper.CrudApiRest(Address.DiscountController).Create(request, token);
+ var request = new CreateDiscountCommand(PageDto.Code,
+ PageDto.Description,
+ PageDto.DiscountPercent,
+ PageDto.DiscountAmount,
+ PageDto.HasCode,
+ PageDto.AmountType,
+ PageDto.Type,
+ PageDto.Count,
+ PageDto.StartDate,
+ PageDto.ExpireDate,
+ PageDto.Immortal,
+ PageDto.PriceFloor,
+ PageDto.HasPriceFloor,
+ PageDto.PriceCeiling,
+ PageDto.HasPriceCeiling,
+ PageDto.IsInfinity,
+ PageDto.UseCount,
+ PageDto.IsForInvitation,
+ PageDto.IsSpecialOffer,
+ PageDto.IsForFirstPurchase,
+ PageDto.ProductId,
+ PageDto.CategoryId);
+ await _restWrapper.CrudDtoApiRest(Address.DiscountController).Create(request, token);
_snackbar.Add($"ساخت تخفیف با موفقیت انجام شد", Severity.Success);
_mudDialog.Close(true);
}
catch (ApiException ex)
{
- var exe = await ex.GetContentAsAsync();
- _snackbar.Add(exe != null ? exe.Message : ex.Content, Severity.Error);
+ if (ex.StatusCode == HttpStatusCode.BadRequest)
+ {
+ var exe = await ex.GetContentAsAsync();
+ _snackbar.Add(exe != null ? exe.Message : ex.Content, Severity.Error);
+ }
+ else
+ _snackbar.Add(ex.Content, Severity.Error);
_mudDialog.Cancel();
}
catch (Exception e)
@@ -248,7 +274,30 @@ public class DiscountActionDialogBoxViewModel : BaseViewModel
if (token == null)
throw new Exception("Token is null");
PageDto.Id = _discountId;
- var request = PageDto.Adapt();
+ var request = new UpdateDiscountCommand(
+ PageDto.Id,
+ PageDto.Code,
+ PageDto.Description,
+ PageDto.DiscountPercent,
+ PageDto.DiscountAmount,
+ PageDto.HasCode,
+ PageDto.AmountType,
+ PageDto.Type,
+ PageDto.Count,
+ PageDto.StartDate,
+ PageDto.ExpireDate,
+ PageDto.Immortal,
+ PageDto.PriceFloor,
+ PageDto.HasPriceFloor,
+ PageDto.PriceCeiling,
+ PageDto.HasPriceCeiling,
+ PageDto.IsInfinity,
+ PageDto.UseCount,
+ PageDto.IsForInvitation,
+ PageDto.IsSpecialOffer,
+ PageDto.IsForFirstPurchase,
+ PageDto.ProductId,
+ PageDto.CategoryId);
await _restWrapper.CrudApiRest(Address.DiscountController).Update(request, token);
_snackbar.Add($"ویرایش تخفیف با موفقیت انجام شد", Severity.Success);
_mudDialog.Close(true);
diff --git a/Netina.AdminPanel.PWA/Dialogs/FastProductCreateDialogBox.razor.cs b/Netina.AdminPanel.PWA/Dialogs/FastProductCreateDialogBox.razor.cs
index 344a7cb..013d9b2 100644
--- a/Netina.AdminPanel.PWA/Dialogs/FastProductCreateDialogBox.razor.cs
+++ b/Netina.AdminPanel.PWA/Dialogs/FastProductCreateDialogBox.razor.cs
@@ -1,4 +1,5 @@
-using Microsoft.AspNetCore.Components.Forms;
+using MediatR;
+using Microsoft.AspNetCore.Components.Forms;
using Netina.Domain.Entities.Brands;
namespace Netina.AdminPanel.PWA.Dialogs;
@@ -141,20 +142,30 @@ public class FastProductCreateDialogBoxViewModel(ISnackbar snackbar, IRestWrappe
}
product.Cost *= 10;
- var command = product.Adapt() with
- {
- BeDisplayed = true,
- BrandId = brand.Id,
- CategoryId = SelectedCategory.Id,
- Files = files,
- Specifications = specifications
- };
- var id = await restWrapper.CrudApiRest(Address.ProductController)
- .Create(command, token);
+ var command = new CreateProductCommand(product.PersianName, product.EnglishName, product.Summery,
+ product.ExpertCheck,
+ product.Tags,
+ product.Warranty,
+ true,
+ product.Cost,
+ product.PackingCost,
+ product.Stock,
+ product.HasExpressDelivery,
+ product.MaxOrderCount,
+ product.IsSpecialOffer,
+ brand.Id,
+ SelectedCategory.Id,
+ null,
+ specifications,
+ files,
+ new Dictionary(),
+ new Dictionary());
+ await restWrapper.CrudApiRest(Address.ProductController).Create(command, token);
}
catch (ApiException ex)
{
- snackbar.Add(ex.Message, Severity.Error);
+ if (ex.StatusCode != HttpStatusCode.OK)
+ snackbar.Add(ex.Message, Severity.Error);
}
catch (Exception e)
{
diff --git a/Netina.AdminPanel.PWA/Dialogs/ProductActionDialogBox.razor b/Netina.AdminPanel.PWA/Dialogs/ProductActionDialogBox.razor
index daef7a9..86932c1 100644
--- a/Netina.AdminPanel.PWA/Dialogs/ProductActionDialogBox.razor
+++ b/Netina.AdminPanel.PWA/Dialogs/ProductActionDialogBox.razor
@@ -182,6 +182,59 @@
+ اطلاعات متا تگ
+ می توانید متا تگ های سئو برای صفحه مورد نظر را وارد کنید
+
+
+
+
+
+
+
+
+
+
+
+ افزودن
+
+
+
+
+
+
+
+
+
+
+
+ حذف
+
+
+
+
+
+
+
+
+
سوالات متداول
می توانید سوالات متداول شهر موردنظر را وارد کنید
@@ -215,7 +268,7 @@
Size="@Size.Small"
Variant="@Variant.Outlined"
Color="@Color.Error"
- OnClick="()=>ViewModel.Faqs.Remove(item.Key)" />
+ OnClick="() => ViewModel.Faqs.Remove(item.Key)"/>
@item.Key
diff --git a/Netina.AdminPanel.PWA/Dialogs/ProductActionDialogBox.razor.cs b/Netina.AdminPanel.PWA/Dialogs/ProductActionDialogBox.razor.cs
index e01d2d4..0ea2c4d 100644
--- a/Netina.AdminPanel.PWA/Dialogs/ProductActionDialogBox.razor.cs
+++ b/Netina.AdminPanel.PWA/Dialogs/ProductActionDialogBox.razor.cs
@@ -1,4 +1,6 @@
-namespace Netina.AdminPanel.PWA.Dialogs;
+using Netina.Domain.Entities.Seo;
+
+namespace Netina.AdminPanel.PWA.Dialogs;
public class ProductActionDialogBoxViewModel : BaseViewModel
{
@@ -89,6 +91,7 @@ public class ProductActionDialogBoxViewModel : BaseViewModel
PageDto = productLDto;
productLDto.Specifications.ForEach(s => Specifications.Add(s));
productLDto.Files.ForEach(f => Files.Add(f));
+ productLDto.MetaTags.ForEach(m => MetaTags.Add(m));
SelectedCategory = new ProductCategorySDto { Id = productLDto.CategoryId, Name = productLDto.CategoryName };
SelectedBrand = new BrandSDto { Id = productLDto.BrandId, PersianName = productLDto.BrandName };
PageDto.IsSpecialOffer = productLDto.IsSpecialOffer;
@@ -193,7 +196,7 @@ public class ProductActionDialogBoxViewModel : BaseViewModel
PageDto.Specifications,
PageDto.Files,
Faqs,
- new Dictionary());
+ MetaTags.ToDictionary(x => x.Type, x => x.Value));
await _restWrapper.CrudApiRest(Address.ProductController).Update(request, token);
_snackbar.Add($"ویرایش محصول {PageDto.PersianName} با موفقیت انجام شد", Severity.Success);
@@ -258,8 +261,8 @@ public class ProductActionDialogBoxViewModel : BaseViewModel
PageDto.Specifications,
PageDto.Files,
Faqs,
- new Dictionary());
- await _restWrapper.CrudApiRest(Address.ProductController).Create(request, token);
+ MetaTags.ToDictionary(x => x.Type, x => x.Value));
+ await _restWrapper.CrudDtoApiRest(Address.ProductController).Create(request, token);
_snackbar.Add($"ساخت محصول {PageDto.PersianName} با موفقیت انجام شد", Severity.Success);
_mudDialog.Close(DialogResult.Ok(true));
@@ -376,6 +379,25 @@ public class ProductActionDialogBoxViewModel : BaseViewModel
}
}
+ public readonly ObservableCollection MetaTags = new();
+ public string MetaTagType { get; set; } = string.Empty;
+ public string MetaTagValue { get; set; } = string.Empty;
+ public void AddMetaTag()
+ {
+ try
+ {
+ if (MetaTagType.IsNullOrEmpty())
+ throw new Exception("لطفا نوع متا مورد نظر را وارد کنید");
+ if (MetaTagValue.IsNullOrEmpty())
+ throw new Exception("لطفا مقدار متا مورد نظر را وارد کنید");
+
+ MetaTags.Add(new MetaTagSDto() { Type = MetaTagType, Value = MetaTagValue });
+ }
+ catch (Exception e)
+ {
+ _snackbar.Add(e.Message, Severity.Error);
+ }
+ }
public void AddSpecification()
{
diff --git a/Netina.AdminPanel.PWA/Dialogs/StorageDialogBox.razor.cs b/Netina.AdminPanel.PWA/Dialogs/StorageDialogBox.razor.cs
index d2fd561..262e531 100644
--- a/Netina.AdminPanel.PWA/Dialogs/StorageDialogBox.razor.cs
+++ b/Netina.AdminPanel.PWA/Dialogs/StorageDialogBox.razor.cs
@@ -115,7 +115,7 @@ public class StorageDialogBoxViewModel : BaseViewModel
IsProcessing = true;
using var memoryStream = new MemoryStream();
var file = obj.File;
- var stream = file.OpenReadStream();
+ var stream = file.OpenReadStream(8000000);
await stream.CopyToAsync(memoryStream);
var fileUpload = new FileUploadRequest
diff --git a/Netina.AdminPanel.PWA/Netina.AdminPanel.PWA.csproj b/Netina.AdminPanel.PWA/Netina.AdminPanel.PWA.csproj
index af06988..c3fc990 100644
--- a/Netina.AdminPanel.PWA/Netina.AdminPanel.PWA.csproj
+++ b/Netina.AdminPanel.PWA/Netina.AdminPanel.PWA.csproj
@@ -5,8 +5,8 @@
enable
enable
service-worker-assets.js
- 1.6.18.28
- 1.6.18.28
+ 1.7.20.34
+ 1.7.20.34
$(MSBuildProjectName)
diff --git a/Netina.AdminPanel.PWA/Pages/BrandsPage.razor b/Netina.AdminPanel.PWA/Pages/BrandsPage.razor
index a454dd6..333d4a7 100644
--- a/Netina.AdminPanel.PWA/Pages/BrandsPage.razor
+++ b/Netina.AdminPanel.PWA/Pages/BrandsPage.razor
@@ -1,10 +1,13 @@
@page "/product/brands"
+@using StringExtensions = Netina.Common.Extensions.StringExtensions
@inject IDialogService DialogService
@inject NavigationManager NavigationManager
@inject IRestWrapper RestWrapper
@inject ISnackbar Snackbar
@inject IUserUtility UserUtility
+@inject IConfiguration Configuration
+@inject IJSRuntime JsRuntime
@@ -25,7 +28,7 @@
-
@if (@context.Item.HasSpecialPage)
@@ -54,15 +56,22 @@
+
+
+
+ OnClick="async () => await ViewModel.EditBrandAsync(context.Item)">
@@ -92,8 +101,13 @@
await base.OnInitializedAsync();
}
-}
-
-@code {
+ private async Task ShowBrand(BrandSDto item)
+ {
+ var webUrl = Configuration.GetValue("WebSiteUrl") ?? string.Empty;
+ var slug = WebUtility.UrlEncode(item.Slug.Replace(' ', '-'));
+ var url = $"{webUrl}/brands/{item.Id}/{slug}";
+ await JsRuntime.InvokeVoidAsync("open", url, "_blank");
+ }
}
+
diff --git a/Netina.AdminPanel.PWA/Pages/BrandsPage.razor.cs b/Netina.AdminPanel.PWA/Pages/BrandsPage.razor.cs
index 0a72ecd..c460932 100644
--- a/Netina.AdminPanel.PWA/Pages/BrandsPage.razor.cs
+++ b/Netina.AdminPanel.PWA/Pages/BrandsPage.razor.cs
@@ -23,6 +23,8 @@ public class BrandsPageViewModel(
var dto = await restWrapper.CrudDtoApiRest(Address.BrandController)
.ReadAll(0);
PageDto = dto;
+ if (PageDto.Count == 10)
+ PageCount = 2;
}
catch (ApiException ex)
{
@@ -108,7 +110,7 @@ public class BrandsPageViewModel(
PageDto.Clear();
var dto = await restWrapper.BrandRestApi.ReadAll(CurrentPage, Search);
dto.ForEach(d => PageDto.Add(d));
- if (PageDto.Count == 20)
+ if (PageDto.Count == 10)
PageCount = 2;
}
catch (ApiException ex)
@@ -149,7 +151,7 @@ public class BrandsPageViewModel(
}
dto.ForEach(d => PageDto.Add(d));
- if (PageDto.Count % 20 == 0)
+ if (PageDto.Count % 10 == 0)
PageCount = CurrentPage + 2;
}
diff --git a/Netina.AdminPanel.PWA/Pages/CategoriesPage.razor.cs b/Netina.AdminPanel.PWA/Pages/CategoriesPage.razor.cs
index 4b37565..2f96270 100644
--- a/Netina.AdminPanel.PWA/Pages/CategoriesPage.razor.cs
+++ b/Netina.AdminPanel.PWA/Pages/CategoriesPage.razor.cs
@@ -152,15 +152,17 @@ public class CategoriesPageViewModel(
public bool ChangeOriginalCategoryVisibility() => OriginalCategoryVisibility = !OriginalCategoryVisibility;
public void AddFastProductCategory(string categoryName, Guid? parentId = null)
{
- if(categoryName.IsNullOrEmpty())
+ if (categoryName.IsNullOrEmpty())
return;
- ProductCategorySDto category = new ProductCategorySDto { Name = categoryName, IsMain = true};
+ ProductCategorySDto category = new ProductCategorySDto { Name = categoryName, IsMain = true };
if (parentId != null)
{
category.IsMain = false;
category.ParentId = parentId.Value;
+
}
- var command = category.Adapt() with{Files = new()};
+
+ var command = new CreateProductCategoryCommand(category.Name, category.Description, category.IsMain, category.ParentId, new(), new Dictionary(), new Dictionary());
Task.Run(async () =>
{
diff --git a/Netina.AdminPanel.PWA/Pages/ProductsPage.razor b/Netina.AdminPanel.PWA/Pages/ProductsPage.razor
index 2bc5f92..8b50aef 100644
--- a/Netina.AdminPanel.PWA/Pages/ProductsPage.razor
+++ b/Netina.AdminPanel.PWA/Pages/ProductsPage.razor
@@ -7,6 +7,8 @@
@inject IUserUtility UserUtility
@inject IRestWrapper RestWrapper
@inject IBrowserViewportService BrowserViewportService
+@inject IConfiguration Configuration
+@inject IJSRuntime JsRuntime
@@ -180,6 +182,12 @@
+
+
("WebSiteUrl") ?? string.Empty;
+ var slug = WebUtility.UrlEncode(item.Slug.Replace(' ', '-'));
+ var url = $"{webUrl}/products/{item.Id}/{slug}";
+ await JsRuntime.InvokeVoidAsync("open", url, "_blank");
+ }
}
diff --git a/Netina.AdminPanel.PWA/Services/RestServices/ICrudApiRest.cs b/Netina.AdminPanel.PWA/Services/RestServices/ICrudApiRest.cs
index bc944c8..0ab3615 100644
--- a/Netina.AdminPanel.PWA/Services/RestServices/ICrudApiRest.cs
+++ b/Netina.AdminPanel.PWA/Services/RestServices/ICrudApiRest.cs
@@ -24,7 +24,7 @@ public interface ICrudApiRest where T : class
public interface ICrudDtoApiRest where T : class where TDto : class
{
[Post("")]
- Task Create([Body] T payload, [Header("Authorization")] string authorization);
+ Task Create([Body] TCreateCommand payload, [Header("Authorization")] string authorization);
[Post("")]
Task Create([Body] TDto payload, [Header("Authorization")] string authorization);
diff --git a/Netina.AdminPanel.PWA/wwwroot/appsettings.Development.json b/Netina.AdminPanel.PWA/wwwroot/appsettings.Development.json
index 937b469..da19de8 100644
--- a/Netina.AdminPanel.PWA/wwwroot/appsettings.Development.json
+++ b/Netina.AdminPanel.PWA/wwwroot/appsettings.Development.json
@@ -16,9 +16,9 @@
//"WebSiteUrl": "https://bonsaigallery.shop",
//"AdminPanelBaseUrl": "https://admin.bonsaigallery.shop",
- //"StorageBaseUrl": "https://storage.bonsaigallery.shop",
+ //"StorageBaseUrl": "https://storage.bonsaigallery.shop/",
//"ApiUrl": "https://api.bonsaigallery.shop/api",
- "IsShop": true
+ //"IsShop": true
//"WebSiteUrl": "https://hamyanedalat.com",
//"AdminPanelBaseUrl": "https://admin.hamyanedalat.com",
diff --git a/ServiceDefaults/Extensions.cs b/ServiceDefaults/Extensions.cs
new file mode 100644
index 0000000..2a3f4e0
--- /dev/null
+++ b/ServiceDefaults/Extensions.cs
@@ -0,0 +1,118 @@
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Diagnostics.HealthChecks;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Diagnostics.HealthChecks;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.ServiceDiscovery;
+using OpenTelemetry;
+using OpenTelemetry.Metrics;
+using OpenTelemetry.Trace;
+
+namespace Microsoft.Extensions.Hosting;
+
+// Adds common .NET Aspire services: service discovery, resilience, health checks, and OpenTelemetry.
+// This project should be referenced by each service project in your solution.
+// To learn more about using this project, see https://aka.ms/dotnet/aspire/service-defaults
+public static class Extensions
+{
+ public static IHostApplicationBuilder AddServiceDefaults(this IHostApplicationBuilder builder)
+ {
+ builder.ConfigureOpenTelemetry();
+
+ builder.AddDefaultHealthChecks();
+
+ builder.Services.AddServiceDiscovery();
+
+ builder.Services.ConfigureHttpClientDefaults(http =>
+ {
+ // Turn on resilience by default
+ http.AddStandardResilienceHandler();
+
+ // Turn on service discovery by default
+ http.AddServiceDiscovery();
+ });
+
+ // Uncomment the following to restrict the allowed schemes for service discovery.
+ // builder.Services.Configure(options =>
+ // {
+ // options.AllowedSchemes = ["https"];
+ // });
+
+ return builder;
+ }
+
+ public static IHostApplicationBuilder ConfigureOpenTelemetry(this IHostApplicationBuilder builder)
+ {
+ builder.Logging.AddOpenTelemetry(logging =>
+ {
+ logging.IncludeFormattedMessage = true;
+ logging.IncludeScopes = true;
+ });
+
+ builder.Services.AddOpenTelemetry()
+ .WithMetrics(metrics =>
+ {
+ metrics.AddAspNetCoreInstrumentation()
+ .AddHttpClientInstrumentation()
+ .AddRuntimeInstrumentation();
+ })
+ .WithTracing(tracing =>
+ {
+ tracing.AddAspNetCoreInstrumentation()
+ // Uncomment the following line to enable gRPC instrumentation (requires the OpenTelemetry.Instrumentation.GrpcNetClient package)
+ //.AddGrpcClientInstrumentation()
+ .AddHttpClientInstrumentation();
+ });
+
+ builder.AddOpenTelemetryExporters();
+
+ return builder;
+ }
+
+ private static IHostApplicationBuilder AddOpenTelemetryExporters(this IHostApplicationBuilder builder)
+ {
+ var useOtlpExporter = !string.IsNullOrWhiteSpace(builder.Configuration["OTEL_EXPORTER_OTLP_ENDPOINT"]);
+
+ if (useOtlpExporter)
+ {
+ builder.Services.AddOpenTelemetry().UseOtlpExporter();
+ }
+
+ // Uncomment the following lines to enable the Azure Monitor exporter (requires the Azure.Monitor.OpenTelemetry.AspNetCore package)
+ //if (!string.IsNullOrEmpty(builder.Configuration["APPLICATIONINSIGHTS_CONNECTION_STRING"]))
+ //{
+ // builder.Services.AddOpenTelemetry()
+ // .UseAzureMonitor();
+ //}
+
+ return builder;
+ }
+
+ public static IHostApplicationBuilder AddDefaultHealthChecks(this IHostApplicationBuilder builder)
+ {
+ builder.Services.AddHealthChecks()
+ // Add a default liveness check to ensure app is responsive
+ .AddCheck("self", () => HealthCheckResult.Healthy(), ["live"]);
+
+ return builder;
+ }
+
+ public static WebApplication MapDefaultEndpoints(this WebApplication app)
+ {
+ // Adding health checks endpoints to applications in non-development environments has security implications.
+ // See https://aka.ms/dotnet/aspire/healthchecks for details before enabling these endpoints in non-development environments.
+ if (app.Environment.IsDevelopment())
+ {
+ // All health checks must pass for app to be considered ready to accept traffic after starting
+ app.MapHealthChecks("/health");
+
+ // Only health checks tagged with the "live" tag must pass for app to be considered alive
+ app.MapHealthChecks("/alive", new HealthCheckOptions
+ {
+ Predicate = r => r.Tags.Contains("live")
+ });
+ }
+
+ return app;
+ }
+}
diff --git a/ServiceDefaults/ServiceDefaults.csproj b/ServiceDefaults/ServiceDefaults.csproj
new file mode 100644
index 0000000..9f4d048
--- /dev/null
+++ b/ServiceDefaults/ServiceDefaults.csproj
@@ -0,0 +1,22 @@
+
+
+
+ net8.0
+ enable
+ enable
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+