From 2463e691b3cb7d6976156c78befb740ae15cc5f6 Mon Sep 17 00:00:00 2001 From: kingdomax Date: Mon, 14 Jul 2025 17:27:31 +0200 Subject: [PATCH 01/15] fix typo --- frontend/src/sections/kanbanboard/hooks/useSignalRTaskHub.ts | 4 ++-- frontend/src/utils/env.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/frontend/src/sections/kanbanboard/hooks/useSignalRTaskHub.ts b/frontend/src/sections/kanbanboard/hooks/useSignalRTaskHub.ts index 48055f0..f69d8f7 100644 --- a/frontend/src/sections/kanbanboard/hooks/useSignalRTaskHub.ts +++ b/frontend/src/sections/kanbanboard/hooks/useSignalRTaskHub.ts @@ -3,7 +3,7 @@ import type { HubConnection } from '@microsoft/signalr'; import { useRef, useLayoutEffect } from 'react'; import { HubConnectionBuilder } from '@microsoft/signalr'; -import { getSeverUrl } from 'src/utils/env'; +import { getServerUrl } from 'src/utils/env'; import { NOTIFY_STATUS } from '../type/kanban-item'; @@ -19,7 +19,7 @@ export const useSignalRTaskHub = ( useLayoutEffect(() => { const connectToHub = async () => { const connection = new HubConnectionBuilder() - .withUrl(`${getSeverUrl()}/taskHub`, { + .withUrl(`${getServerUrl()}/taskHub`, { withCredentials: true, // Required }) .withAutomaticReconnect() diff --git a/frontend/src/utils/env.ts b/frontend/src/utils/env.ts index c90db06..36510bb 100644 --- a/frontend/src/utils/env.ts +++ b/frontend/src/utils/env.ts @@ -1,8 +1,8 @@ // TODO: Hack it for now, need to refactor to use .env files for cleaner approach later. // But need to think carefully and that might involve changed on dockerfile, docker-compose.yml, docker-compose.prod.yml, and ci.yml !! -export const getSeverUrl = (): string => +export const getServerUrl = (): string => window.location.hostname.includes('localhost') ? 'http://localhost:5070' : 'http://131.189.90.113:5070'; -export const getApiUrl = (): string => `${getSeverUrl()}/api/v1`; +export const getApiUrl = (): string => `${getServerUrl()}/api/v1`; From fafdbcf04e616eca85c06c4c3b1f15d31d351d82 Mon Sep 17 00:00:00 2001 From: kingdomax Date: Thu, 17 Jul 2025 12:49:20 +0200 Subject: [PATCH 02/15] add Serilog and Sink package in CoreAPI --- backend/TaskSync.csproj | 2 ++ 1 file changed, 2 insertions(+) diff --git a/backend/TaskSync.csproj b/backend/TaskSync.csproj index cfaac4e..d1bed65 100644 --- a/backend/TaskSync.csproj +++ b/backend/TaskSync.csproj @@ -19,6 +19,8 @@ all + + runtime; build; native; contentfiles; analyzers; buildtransitive all From ea1c9a68e5ac25f50e73439c11833009433f4dcc Mon Sep 17 00:00:00 2001 From: kingdomax Date: Fri, 18 Jul 2025 16:10:55 +0200 Subject: [PATCH 03/15] add OpenTelemetry SDK to Core API --- backend/TaskSync.csproj | 2 ++ 1 file changed, 2 insertions(+) diff --git a/backend/TaskSync.csproj b/backend/TaskSync.csproj index d1bed65..289f815 100644 --- a/backend/TaskSync.csproj +++ b/backend/TaskSync.csproj @@ -19,6 +19,8 @@ all + + From 84ca31c6408e7ea99f27c3390a53124d6c62531b Mon Sep 17 00:00:00 2001 From: kingdomax Date: Mon, 21 Jul 2025 21:32:34 +0200 Subject: [PATCH 04/15] Configure Metrics --- backend/Program.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/backend/Program.cs b/backend/Program.cs index dbc4034..3fd20be 100644 --- a/backend/Program.cs +++ b/backend/Program.cs @@ -1,5 +1,7 @@ using System.Text.Json.Serialization; +using OpenTelemetry.Metrics; + using TaskSync.Infrastructure.Configurations; using TaskSync.MiddleWares; using TaskSync.SignalR; @@ -21,6 +23,11 @@ builder.Services.ConfigureCors(); builder.Services.ConfigureJwt(builder.Configuration); builder.Services.ConfigureDependencyInjection(builder.Configuration); +builder.Services.AddOpenTelemetry() + .WithMetrics(metrics => + { + metrics.AddAspNetCoreInstrumentation(); // .AddPrometheusExporter(); + }); // ------------------------------------------------------------------------------- // -------------- Configure/Order the request pipeline (Middleware) -------------- @@ -39,5 +46,6 @@ app.UseMiddleware(); app.MapControllers(); // or MapControllerRoute, inside it call UseRouting() app.MapHub("/taskHub"); +// app.UseOpenTelemetryPrometheusScrapingEndpoint(); // exposes /metrics app.Run(); // ----------------------------------------------------------------------- From 902c9033ed5f30589229a0301b1953e0aa90ff28 Mon Sep 17 00:00:00 2001 From: kingdomax Date: Tue, 22 Jul 2025 16:59:43 +0200 Subject: [PATCH 05/15] make change to the packages --- backend/Program.cs | 8 -------- backend/TaskSync.csproj | 5 +++-- .../src/sections/kanbanboard/view/kanban-board-view.tsx | 1 + 3 files changed, 4 insertions(+), 10 deletions(-) diff --git a/backend/Program.cs b/backend/Program.cs index 3fd20be..dbc4034 100644 --- a/backend/Program.cs +++ b/backend/Program.cs @@ -1,7 +1,5 @@ using System.Text.Json.Serialization; -using OpenTelemetry.Metrics; - using TaskSync.Infrastructure.Configurations; using TaskSync.MiddleWares; using TaskSync.SignalR; @@ -23,11 +21,6 @@ builder.Services.ConfigureCors(); builder.Services.ConfigureJwt(builder.Configuration); builder.Services.ConfigureDependencyInjection(builder.Configuration); -builder.Services.AddOpenTelemetry() - .WithMetrics(metrics => - { - metrics.AddAspNetCoreInstrumentation(); // .AddPrometheusExporter(); - }); // ------------------------------------------------------------------------------- // -------------- Configure/Order the request pipeline (Middleware) -------------- @@ -46,6 +39,5 @@ app.UseMiddleware(); app.MapControllers(); // or MapControllerRoute, inside it call UseRouting() app.MapHub("/taskHub"); -// app.UseOpenTelemetryPrometheusScrapingEndpoint(); // exposes /metrics app.Run(); // ----------------------------------------------------------------------- diff --git a/backend/TaskSync.csproj b/backend/TaskSync.csproj index 289f815..7e86ed6 100644 --- a/backend/TaskSync.csproj +++ b/backend/TaskSync.csproj @@ -19,10 +19,11 @@ all + - - + + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/frontend/src/sections/kanbanboard/view/kanban-board-view.tsx b/frontend/src/sections/kanbanboard/view/kanban-board-view.tsx index 9552f41..a7c2a44 100644 --- a/frontend/src/sections/kanbanboard/view/kanban-board-view.tsx +++ b/frontend/src/sections/kanbanboard/view/kanban-board-view.tsx @@ -66,6 +66,7 @@ export const KanbanBoardView = () => { return ( + i.id == selectedItem) ?? null} onSelect={handleSelectItem} From f04f3fee983109244670d019d876d1fa1f69b65c Mon Sep 17 00:00:00 2001 From: kingdomax Date: Wed, 23 Jul 2025 20:46:34 +0200 Subject: [PATCH 06/15] Configure opentelemetry SDK in Core API --- .../OpenTelemetryConfiguration.cs | 60 +++++++++++++++++++ backend/Program.cs | 1 + backend/TaskSync.csproj | 4 +- 3 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 backend/Infrastructure/Configurations/OpenTelemetryConfiguration.cs diff --git a/backend/Infrastructure/Configurations/OpenTelemetryConfiguration.cs b/backend/Infrastructure/Configurations/OpenTelemetryConfiguration.cs new file mode 100644 index 0000000..2d57e2c --- /dev/null +++ b/backend/Infrastructure/Configurations/OpenTelemetryConfiguration.cs @@ -0,0 +1,60 @@ +using OpenTelemetry.Logs; +using OpenTelemetry.Metrics; +using OpenTelemetry.Resources; +using OpenTelemetry.Trace; + +namespace TaskSync.Infrastructure.Configurations +{ + public static class OpenTelemetryConfiguration + { + public static void ConfigureOpenTelemetry(this WebApplicationBuilder builder) + { + // Shared resource attributes (customize for app) + var otelResource = ResourceBuilder.CreateDefault().AddService("Core.API", serviceVersion: "1.0.0"); + + // --- Add OpenTelemetry for Tracing and Metrics --- + builder.Services.AddOpenTelemetry().ConfigureResource(rb => rb.AddService("Core.API")) // todo-moch: don't hardcode this + .WithTracing(tracing => + { + tracing + .AddAspNetCoreInstrumentation() + .AddHttpClientInstrumentation() + .AddSqlClientInstrumentation() + .AddOtlpExporter(opt => + { + opt.Endpoint = new Uri("http://otel-collector:4317"); // Use Docker service name! and todo-moch: don't hardcode this + opt.Protocol = OpenTelemetry.Exporter.OtlpExportProtocol.Grpc; + }); + }) + .WithMetrics(metrics => + { + metrics + .AddAspNetCoreInstrumentation() // automatic telemetry for incoming HTTP requests e.g. "/api/projects/1/tasks" + .AddHttpClientInstrumentation() // automatic telemetry for outgoing HTTP requests e.g. HttpClient usage + .AddRuntimeInstrumentation() // Garbage collection counts, Thread pool usage, Memory pressure + .AddProcessInstrumentation() // CPU usage, Memory, Thread count + .AddOtlpExporter(opt => + { + opt.Endpoint = new Uri("http://otel-collector:4317"); + opt.Protocol = OpenTelemetry.Exporter.OtlpExportProtocol.Grpc; + }); + }); + + // --- Add OpenTelemetry Logging --- + builder.Logging.ClearProviders(); + builder.Logging.AddOpenTelemetry(loggerOptions => + { + loggerOptions.IncludeScopes = true; + loggerOptions.IncludeFormattedMessage = true; + loggerOptions.ParseStateValues = true; + + loggerOptions.SetResourceBuilder(otelResource); + + loggerOptions.AddOtlpExporter(otlpOptions => + { + otlpOptions.Endpoint = new Uri("http://otel-collector:4317"); // or 4318 for HTTP + }); + }); + } + } +} diff --git a/backend/Program.cs b/backend/Program.cs index dbc4034..ff77606 100644 --- a/backend/Program.cs +++ b/backend/Program.cs @@ -15,6 +15,7 @@ builder.Services.AddHttpClient(); builder.Services.AddRateLimiter(); builder.Services.AddMemoryCache(options => options.SizeLimit = 100); +builder.ConfigureOpenTelemetry(); builder.Services.ConfigureAppSettings(builder.Configuration); builder.Services.ConfigureApiVersion(); builder.Services.ConfigureResponseCompression(); diff --git a/backend/TaskSync.csproj b/backend/TaskSync.csproj index 7e86ed6..55bc736 100644 --- a/backend/TaskSync.csproj +++ b/backend/TaskSync.csproj @@ -18,11 +18,13 @@ runtime; build; native; contentfiles; analyzers; buildtransitive all - + + + runtime; build; native; contentfiles; analyzers; buildtransitive From 4c53b5eac9ce69a7fda4583bd8d4a412c929788e Mon Sep 17 00:00:00 2001 From: kingdomax Date: Thu, 24 Jul 2025 11:51:11 +0200 Subject: [PATCH 07/15] Add Otel Collector Config --- .../OpenTelemetryConfiguration.cs | 5 ++-- docker-compose-partial.yml | 0 otel-collector-config.yaml | 27 +++++++++++++++++++ 3 files changed, 30 insertions(+), 2 deletions(-) create mode 100644 docker-compose-partial.yml create mode 100644 otel-collector-config.yaml diff --git a/backend/Infrastructure/Configurations/OpenTelemetryConfiguration.cs b/backend/Infrastructure/Configurations/OpenTelemetryConfiguration.cs index 2d57e2c..50dd9e6 100644 --- a/backend/Infrastructure/Configurations/OpenTelemetryConfiguration.cs +++ b/backend/Infrastructure/Configurations/OpenTelemetryConfiguration.cs @@ -5,6 +5,7 @@ namespace TaskSync.Infrastructure.Configurations { + // Note: The OpenTelemetry collector should be running in the same Docker network as the API service. public static class OpenTelemetryConfiguration { public static void ConfigureOpenTelemetry(this WebApplicationBuilder builder) @@ -12,7 +13,7 @@ public static void ConfigureOpenTelemetry(this WebApplicationBuilder builder) // Shared resource attributes (customize for app) var otelResource = ResourceBuilder.CreateDefault().AddService("Core.API", serviceVersion: "1.0.0"); - // --- Add OpenTelemetry for Tracing and Metrics --- + // Add OpenTelemetry for Tracing and Metrics builder.Services.AddOpenTelemetry().ConfigureResource(rb => rb.AddService("Core.API")) // todo-moch: don't hardcode this .WithTracing(tracing => { @@ -40,7 +41,7 @@ public static void ConfigureOpenTelemetry(this WebApplicationBuilder builder) }); }); - // --- Add OpenTelemetry Logging --- + // Add OpenTelemetry Logging builder.Logging.ClearProviders(); builder.Logging.AddOpenTelemetry(loggerOptions => { diff --git a/docker-compose-partial.yml b/docker-compose-partial.yml new file mode 100644 index 0000000..e69de29 diff --git a/otel-collector-config.yaml b/otel-collector-config.yaml new file mode 100644 index 0000000..527dbea --- /dev/null +++ b/otel-collector-config.yaml @@ -0,0 +1,27 @@ +receivers: + otlp: + protocols: + grpc: + http: + +exporters: + prometheus: + endpoint: "0.0.0.0:8888" + loki: + endpoint: "http://loki:3100/loki/api/v1/push" + tempo: + endpoint: "tempo:4317" + tls: + insecure: true + +service: + pipelines: + traces: + receivers: [otlp] + exporters: [tempo] + metrics: + receivers: [otlp] + exporters: [prometheus] + logs: + receivers: [otlp] + exporters: [loki] From ab2abd75d1fcc128ca8e0de7a8322e683c60cf66 Mon Sep 17 00:00:00 2001 From: kingdomax Date: Fri, 25 Jul 2025 16:50:40 +0200 Subject: [PATCH 08/15] scalfold telemetry modules --- docker-compose-partial.yml | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/docker-compose-partial.yml b/docker-compose-partial.yml index e69de29..e924b9b 100644 --- a/docker-compose-partial.yml +++ b/docker-compose-partial.yml @@ -0,0 +1,30 @@ +version: "3.8" +services: + otel-collector: + image: otel/opentelemetry-collector-contrib:0.97.0 + command: ["--config=/etc/otel-collector-config.yaml"] + volumes: + - ./otel-collector-config.yaml:/etc/otel-collector-config.yaml + ports: + - "4317:4317" # OTLP gRPC + - "4318:4318" # OTLP HTTP + - "8888:8888" # Collector metrics + prometheus: + image: prom/prometheus + ports: ["9090:9090"] + volumes: + - ./prometheus.yml:/etc/prometheus/prometheus.yml + tempo: + image: grafana/tempo:2.4.1 + ports: + - "3200:3200" + loki: + image: grafana/loki:2.9.0 + ports: + - "3100:3100" + grafana: + image: grafana/grafana + ports: + - "3000:3000" + environment: + - GF_SECURITY_ADMIN_PASSWORD=admin From 485e3a42c9a1e7a5499847961f5bdeb7128d873b Mon Sep 17 00:00:00 2001 From: kingdomax Date: Sat, 26 Jul 2025 17:12:58 +0200 Subject: [PATCH 09/15] revert docker-compose-partial.yml --- docker-compose-partial.yml | 30 ------------------------------ 1 file changed, 30 deletions(-) diff --git a/docker-compose-partial.yml b/docker-compose-partial.yml index e924b9b..e69de29 100644 --- a/docker-compose-partial.yml +++ b/docker-compose-partial.yml @@ -1,30 +0,0 @@ -version: "3.8" -services: - otel-collector: - image: otel/opentelemetry-collector-contrib:0.97.0 - command: ["--config=/etc/otel-collector-config.yaml"] - volumes: - - ./otel-collector-config.yaml:/etc/otel-collector-config.yaml - ports: - - "4317:4317" # OTLP gRPC - - "4318:4318" # OTLP HTTP - - "8888:8888" # Collector metrics - prometheus: - image: prom/prometheus - ports: ["9090:9090"] - volumes: - - ./prometheus.yml:/etc/prometheus/prometheus.yml - tempo: - image: grafana/tempo:2.4.1 - ports: - - "3200:3200" - loki: - image: grafana/loki:2.9.0 - ports: - - "3100:3100" - grafana: - image: grafana/grafana - ports: - - "3000:3000" - environment: - - GF_SECURITY_ADMIN_PASSWORD=admin From 0d72e9cbc8f45d68050d0a32451f621907557fd7 Mon Sep 17 00:00:00 2001 From: kingdomax Date: Sun, 27 Jul 2025 17:53:35 +0200 Subject: [PATCH 10/15] scalffold docker-compose-partial for necessary components to run on local dev --- docker-compose-partial.yml | 45 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/docker-compose-partial.yml b/docker-compose-partial.yml index e69de29..d884256 100644 --- a/docker-compose-partial.yml +++ b/docker-compose-partial.yml @@ -0,0 +1,45 @@ +services: + postgres: + image: postgres:17 + container_name: tasksync-postgres + restart: on-failure # Restart container automatically if it crashes + ports: + - "5433:5432" + environment: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: 123456789 + POSTGRES_DB: tasksync # Database name to auto-create + volumes: + - pgdata:/var/lib/postgresql/data # Persist Postgres data outside the container + + otel-collector: + image: otel/opentelemetry-collector-contrib:0.97.0 + command: ["--config=/etc/otel-collector-config.yaml"] + volumes: + - ./otel-collector-config.yaml:/etc/otel-collector-config.yaml + ports: + - "4317:4317" # OTLP gRPC + - "4318:4318" # OTLP HTTP + - "8888:8888" # Collector metrics + prometheus: + image: prom/prometheus + ports: ["9090:9090"] + volumes: + - ./prometheus.yml:/etc/prometheus/prometheus.yml + tempo: + image: grafana/tempo:2.4.1 + ports: + - "3200:3200" + loki: + image: grafana/loki:2.9.0 + ports: + - "3100:3100" + grafana: + image: grafana/grafana + ports: + - "3000:3000" + environment: + - GF_SECURITY_ADMIN_PASSWORD=admin + +volumes: + pgdata: # Named volume to store Postgres data (managed by Docker) From 4b26cc6208c97d9e13afaabf7a11d4bcf48d8ea4 Mon Sep 17 00:00:00 2001 From: kingdomax Date: Mon, 28 Jul 2025 17:13:43 +0200 Subject: [PATCH 11/15] move all appsettings to POCO pattern --- backend/ExternalApi/GamificationApi.cs | 14 ++--- .../Configurations/CorsConfiguration.cs | 20 +++--- .../DependencyInjectionConfiguration.cs | 8 ++- .../Configurations/HttpClientConfiguration.cs | 22 +++++++ .../Configurations/JwtConfiguration.cs | 5 +- .../OpenTelemetryConfiguration.cs | 23 ++++--- .../Configurations/SettingsConfiguration.cs | 3 + .../Settings/FrontendSettings.cs | 7 +++ .../Settings/OtelCollectorSettings.cs | 7 +++ .../Settings/PostgreSqlSettings.cs | 12 ++++ backend/Program.cs | 8 +-- backend/appsettings.json | 63 +++++++++++-------- 12 files changed, 131 insertions(+), 61 deletions(-) create mode 100644 backend/Infrastructure/Configurations/HttpClientConfiguration.cs create mode 100644 backend/Infrastructure/Settings/FrontendSettings.cs create mode 100644 backend/Infrastructure/Settings/OtelCollectorSettings.cs create mode 100644 backend/Infrastructure/Settings/PostgreSqlSettings.cs diff --git a/backend/ExternalApi/GamificationApi.cs b/backend/ExternalApi/GamificationApi.cs index 030f81f..2947425 100644 --- a/backend/ExternalApi/GamificationApi.cs +++ b/backend/ExternalApi/GamificationApi.cs @@ -1,8 +1,5 @@ -using Microsoft.Extensions.Options; - -using TaskSync.ExternalApi.Interfaces; +using TaskSync.ExternalApi.Interfaces; using TaskSync.Infrastructure.Http.Interface; -using TaskSync.Infrastructure.Settings; using TaskSync.Models.Dto; using TaskStatus = TaskSync.Enums.TASK_STATUS; @@ -11,16 +8,14 @@ namespace TaskSync.ExternalApi { public class GamificationApi : IGamificationApi { - private readonly GamificationApiSettings _gamApiSettings; private readonly IHttpContextReader _httpContextReader; private readonly HttpClient _httpClient; private readonly ILogger _logger; - public GamificationApi(IOptions options, IHttpContextReader httpContextReader, IHttpClientFactory httpClientFactory, ILogger logger) + public GamificationApi(IHttpContextReader httpContextReader, IHttpClientFactory httpClientFactory, ILogger logger) { - _gamApiSettings = options.Value; _httpContextReader = httpContextReader; - _httpClient = httpClientFactory.CreateClient(); + _httpClient = httpClientFactory.CreateClient("GamificationApi"); _logger = logger; } @@ -28,7 +23,7 @@ public async Task UpdatePoint(int taskId, TaskStatus status) { try { - var httpMessage = new HttpRequestMessage(HttpMethod.Post, $"{_gamApiSettings.BaseUrl}/points") + var httpMessage = new HttpRequestMessage(HttpMethod.Post, $"{_httpClient.BaseAddress}/points") { Content = JsonContent.Create(new CreatePointDto() { @@ -37,7 +32,6 @@ public async Task UpdatePoint(int taskId, TaskStatus status) UserId = _httpContextReader.GetUserId(), }), }; - httpMessage.Headers.Add("x-gamapi-auth", "true"); await _httpClient.SendAsync(httpMessage); } diff --git a/backend/Infrastructure/Configurations/CorsConfiguration.cs b/backend/Infrastructure/Configurations/CorsConfiguration.cs index 886c48c..362f0dc 100644 --- a/backend/Infrastructure/Configurations/CorsConfiguration.cs +++ b/backend/Infrastructure/Configurations/CorsConfiguration.cs @@ -1,20 +1,24 @@ -namespace TaskSync.Infrastructure.Configurations +using Microsoft.Extensions.Options; + +using TaskSync.Infrastructure.Settings; + +namespace TaskSync.Infrastructure.Configurations { public static class CorsConfiguration { public static IServiceCollection ConfigureCors(this IServiceCollection services) { + var provider = services.BuildServiceProvider(); + services.AddCors(options => { options.AddDefaultPolicy(policy => { - policy.WithOrigins( - "http://localhost:3039", // Dev - "http://131.189.90.113:3039") // Production - .AllowAnyHeader() - .AllowAnyMethod() - .AllowCredentials() - .SetPreflightMaxAge(TimeSpan.FromMinutes(10)); // Cache preflight 10 min + policy.WithOrigins(provider.GetRequiredService>().Value.Urls) + .AllowAnyHeader() + .AllowAnyMethod() + .AllowCredentials() + .SetPreflightMaxAge(TimeSpan.FromMinutes(10)); // Cache preflight 10 min }); }); diff --git a/backend/Infrastructure/Configurations/DependencyInjectionConfiguration.cs b/backend/Infrastructure/Configurations/DependencyInjectionConfiguration.cs index 46d2f8a..03c999a 100644 --- a/backend/Infrastructure/Configurations/DependencyInjectionConfiguration.cs +++ b/backend/Infrastructure/Configurations/DependencyInjectionConfiguration.cs @@ -1,4 +1,5 @@ using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Options; using TaskSync.ExternalApi; using TaskSync.ExternalApi.Interfaces; @@ -6,6 +7,7 @@ using TaskSync.Infrastructure.Caching.Interfaces; using TaskSync.Infrastructure.Http; using TaskSync.Infrastructure.Http.Interface; +using TaskSync.Infrastructure.Settings; using TaskSync.Repositories; using TaskSync.Repositories.Entities; using TaskSync.Repositories.Interfaces; @@ -18,10 +20,12 @@ namespace TaskSync.Infrastructure.Configurations { public static class DependencyInjectionConfiguration { - public static IServiceCollection ConfigureDependencyInjection(this IServiceCollection services, IConfiguration configuration) + public static IServiceCollection ConfigureDependencyInjection(this IServiceCollection services) { + var provider = services.BuildServiceProvider(); + services.AddDbContext(options => - options.UseNpgsql(configuration.GetConnectionString("DefaultConnection")) + options.UseNpgsql(provider.GetRequiredService>().Value.ConnectionString) ); services.AddSingleton(); diff --git a/backend/Infrastructure/Configurations/HttpClientConfiguration.cs b/backend/Infrastructure/Configurations/HttpClientConfiguration.cs new file mode 100644 index 0000000..18f98d0 --- /dev/null +++ b/backend/Infrastructure/Configurations/HttpClientConfiguration.cs @@ -0,0 +1,22 @@ +using Microsoft.Extensions.Options; + +using TaskSync.Infrastructure.Settings; + +namespace TaskSync.Infrastructure.Configurations +{ + public static class HttpClientConfiguration + { + public static IServiceCollection ConfigureHttpClient(this IServiceCollection services) + { + var provider = services.BuildServiceProvider(); + + services.AddHttpClient("GamificationApi", client => + { + client.BaseAddress = new Uri(provider.GetRequiredService>().Value.BaseUrl); + client.DefaultRequestHeaders.Add("x-gamapi-auth", "true"); + }); + + return services; + } + } +} diff --git a/backend/Infrastructure/Configurations/JwtConfiguration.cs b/backend/Infrastructure/Configurations/JwtConfiguration.cs index be1c02e..8fde5b3 100644 --- a/backend/Infrastructure/Configurations/JwtConfiguration.cs +++ b/backend/Infrastructure/Configurations/JwtConfiguration.cs @@ -10,10 +10,9 @@ namespace TaskSync.Infrastructure.Configurations { public static class JwtConfiguration { - public static IServiceCollection ConfigureJwt(this IServiceCollection services, IConfiguration configuration) + public static IServiceCollection ConfigureJwt(this IServiceCollection services) { - var provider = services.BuildServiceProvider(); - var jwtSettings = provider.GetRequiredService>().Value; + var jwtSettings = services.BuildServiceProvider().GetRequiredService>().Value; services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(options => diff --git a/backend/Infrastructure/Configurations/OpenTelemetryConfiguration.cs b/backend/Infrastructure/Configurations/OpenTelemetryConfiguration.cs index 50dd9e6..dd0e7bb 100644 --- a/backend/Infrastructure/Configurations/OpenTelemetryConfiguration.cs +++ b/backend/Infrastructure/Configurations/OpenTelemetryConfiguration.cs @@ -1,20 +1,27 @@ -using OpenTelemetry.Logs; +using Microsoft.Extensions.Options; + +using OpenTelemetry.Logs; using OpenTelemetry.Metrics; using OpenTelemetry.Resources; using OpenTelemetry.Trace; +using TaskSync.Infrastructure.Settings; + namespace TaskSync.Infrastructure.Configurations { - // Note: The OpenTelemetry collector should be running in the same Docker network as the API service. public static class OpenTelemetryConfiguration { - public static void ConfigureOpenTelemetry(this WebApplicationBuilder builder) + public static void ConfigureTelemetry(this WebApplicationBuilder builder) { + var provider = builder.Services.BuildServiceProvider(); + var appInfo = provider.GetRequiredService>().Value; + var otelSettings = provider.GetRequiredService>().Value; + // Shared resource attributes (customize for app) - var otelResource = ResourceBuilder.CreateDefault().AddService("Core.API", serviceVersion: "1.0.0"); + var otelResource = ResourceBuilder.CreateDefault().AddService(appInfo.AppName, serviceVersion: "1.0.0"); // Add OpenTelemetry for Tracing and Metrics - builder.Services.AddOpenTelemetry().ConfigureResource(rb => rb.AddService("Core.API")) // todo-moch: don't hardcode this + builder.Services.AddOpenTelemetry().ConfigureResource(rb => rb.AddService(appInfo.AppName)) .WithTracing(tracing => { tracing @@ -23,7 +30,7 @@ public static void ConfigureOpenTelemetry(this WebApplicationBuilder builder) .AddSqlClientInstrumentation() .AddOtlpExporter(opt => { - opt.Endpoint = new Uri("http://otel-collector:4317"); // Use Docker service name! and todo-moch: don't hardcode this + opt.Endpoint = new Uri(otelSettings.BaseUrl); // Use Docker service name! and todo-moch: don't hardcode this opt.Protocol = OpenTelemetry.Exporter.OtlpExportProtocol.Grpc; }); }) @@ -36,7 +43,7 @@ public static void ConfigureOpenTelemetry(this WebApplicationBuilder builder) .AddProcessInstrumentation() // CPU usage, Memory, Thread count .AddOtlpExporter(opt => { - opt.Endpoint = new Uri("http://otel-collector:4317"); + opt.Endpoint = new Uri(otelSettings.BaseUrl); // http://otel-collector:4317 opt.Protocol = OpenTelemetry.Exporter.OtlpExportProtocol.Grpc; }); }); @@ -53,7 +60,7 @@ public static void ConfigureOpenTelemetry(this WebApplicationBuilder builder) loggerOptions.AddOtlpExporter(otlpOptions => { - otlpOptions.Endpoint = new Uri("http://otel-collector:4317"); // or 4318 for HTTP + otlpOptions.Endpoint = new Uri(otelSettings.BaseUrl); }); }); } diff --git a/backend/Infrastructure/Configurations/SettingsConfiguration.cs b/backend/Infrastructure/Configurations/SettingsConfiguration.cs index 9985d37..dcb6d40 100644 --- a/backend/Infrastructure/Configurations/SettingsConfiguration.cs +++ b/backend/Infrastructure/Configurations/SettingsConfiguration.cs @@ -8,6 +8,9 @@ public static IServiceCollection ConfigureAppSettings(this IServiceCollection se { services.Configure(configuration.GetSection("AppInfo")); services.Configure(configuration.GetSection("JwtSettings")); + services.Configure(configuration.GetSection("Frontend")); + services.Configure(configuration.GetSection("PostgreSql")); + services.Configure(configuration.GetSection("OtelCollector")); services.Configure(configuration.GetSection("MiddlewareSettings")); services.Configure(configuration.GetSection("GamificationApi")); diff --git a/backend/Infrastructure/Settings/FrontendSettings.cs b/backend/Infrastructure/Settings/FrontendSettings.cs new file mode 100644 index 0000000..9ffb480 --- /dev/null +++ b/backend/Infrastructure/Settings/FrontendSettings.cs @@ -0,0 +1,7 @@ +namespace TaskSync.Infrastructure.Settings +{ + public class FrontendSettings + { + public required string[] Urls { get; set; } + } +} diff --git a/backend/Infrastructure/Settings/OtelCollectorSettings.cs b/backend/Infrastructure/Settings/OtelCollectorSettings.cs new file mode 100644 index 0000000..ac997a8 --- /dev/null +++ b/backend/Infrastructure/Settings/OtelCollectorSettings.cs @@ -0,0 +1,7 @@ +namespace TaskSync.Infrastructure.Settings +{ + public class OtelCollectorSettings + { + public required string BaseUrl { get; set; } + } +} diff --git a/backend/Infrastructure/Settings/PostgreSqlSettings.cs b/backend/Infrastructure/Settings/PostgreSqlSettings.cs new file mode 100644 index 0000000..a82c70e --- /dev/null +++ b/backend/Infrastructure/Settings/PostgreSqlSettings.cs @@ -0,0 +1,12 @@ +namespace TaskSync.Infrastructure.Settings +{ + public class PostgreSqlSettings + { + public required string Host { get; set; } + public required string Port { get; set; } + public required string Database { get; set; } + public required string Username { get; set; } + public required string Password { get; set; } + public string ConnectionString => $"Host={Host};Port={Port};Database={Database};Username={Username};Password={Password}"; + } +} diff --git a/backend/Program.cs b/backend/Program.cs index ff77606..5a906c1 100644 --- a/backend/Program.cs +++ b/backend/Program.cs @@ -15,13 +15,13 @@ builder.Services.AddHttpClient(); builder.Services.AddRateLimiter(); builder.Services.AddMemoryCache(options => options.SizeLimit = 100); -builder.ConfigureOpenTelemetry(); builder.Services.ConfigureAppSettings(builder.Configuration); -builder.Services.ConfigureApiVersion(); +builder.Services.ConfigureDependencyInjection(); builder.Services.ConfigureResponseCompression(); +builder.Services.ConfigureApiVersion(); builder.Services.ConfigureCors(); -builder.Services.ConfigureJwt(builder.Configuration); -builder.Services.ConfigureDependencyInjection(builder.Configuration); +builder.Services.ConfigureJwt(); +builder.ConfigureTelemetry(); // ------------------------------------------------------------------------------- // -------------- Configure/Order the request pipeline (Middleware) -------------- diff --git a/backend/appsettings.json b/backend/appsettings.json index b15dad9..c13d60a 100644 --- a/backend/appsettings.json +++ b/backend/appsettings.json @@ -1,29 +1,40 @@ { - "AppInfo": { - "AppName": "TaskSync Core API", - "Environment": "Production" - }, - "Logging": { - "LogLevel": { - "Default": "Information", - "Microsoft.AspNetCore": "Warning", - "Microsoft.EntityFrameworkCore.Database.Command": "Warning" - } - }, - "AllowedHosts": "*", - "ConnectionStrings": { - "DefaultConnection": "Host=localhost;Port=5433;Database=tasksync;Username=postgres;Password=123456789" - }, - "JwtSettings": { - "SecretKey": "taskSyncSuperSecretKeyThatIsLong123!", - "Issuer": "TaskSync", - "Audience": "TaskSyncUsers", - "ExpirationMinutes": 180 - }, - "MiddlewareSettings": { - "ExcludedPaths": ["/taskHub", "/signalr", "/health"] - }, - "GamificationApi": { - "BaseUrl": "http://localhost:3000" + "AppInfo": { + "AppName": "Core.API", + "Environment": "Production" + }, + "AllowedHosts": "*", + "Frontend": { + "Urls": ["http://localhost:3039", "http://131.189.90.113:3039"] // dev and production + }, + "PostgreSql": { + "Host": "localhost", + "Port": "5433", + "Database": "tasksync", + "Username": "postgres", + "Password": "123456789" + // "DefaultConnection": "Host=localhost;Port=5433;Database=tasksync;Username=postgres;Password=123456789" + }, + "GamificationApi": { + "BaseUrl": "http://localhost:3000" + }, + "OtelCollector": { + "BaseUrl": "http://localhost:4317" + }, + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning", + "Microsoft.EntityFrameworkCore.Database.Command": "Warning" } + }, + "JwtSettings": { + "SecretKey": "taskSyncSuperSecretKeyThatIsLong123!", + "Issuer": "TaskSync", + "Audience": "TaskSyncUsers", + "ExpirationMinutes": 180 + }, + "MiddlewareSettings": { + "ExcludedPaths": [ "/taskHub", "/signalr", "/health" ] + } } From 241e50e250e57f20f07a9dd53fd0000a33fa4f80 Mon Sep 17 00:00:00 2001 From: kingdomax Date: Tue, 29 Jul 2025 16:44:51 +0200 Subject: [PATCH 12/15] add AsNoTracking() for read-only query --- backend/Repositories/ProjectRepository.cs | 2 +- backend/Repositories/TaskRepository.cs | 2 +- backend/Repositories/UserRepository.cs | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/backend/Repositories/ProjectRepository.cs b/backend/Repositories/ProjectRepository.cs index 0c85b42..6d7d33a 100644 --- a/backend/Repositories/ProjectRepository.cs +++ b/backend/Repositories/ProjectRepository.cs @@ -12,7 +12,7 @@ public class ProjectRepository : IProjectRepository public async Task GetByIdAsync(int projectId) { - return await _dbContext.Projects.FirstOrDefaultAsync(x => x.Id == projectId); + return await _dbContext.Projects.AsNoTracking().FirstOrDefaultAsync(x => x.Id == projectId); } } } diff --git a/backend/Repositories/TaskRepository.cs b/backend/Repositories/TaskRepository.cs index 1d769e1..398ab49 100644 --- a/backend/Repositories/TaskRepository.cs +++ b/backend/Repositories/TaskRepository.cs @@ -18,7 +18,7 @@ public async Task BeginTransactionAsync() public async Task?> GetAsync(int projectId) { - return await _dbContext.Tasks.Where(x => x.ProjectId == projectId).AsNoTracking().ToListAsync(); + return await _dbContext.Tasks.AsNoTracking().Where(x => x.ProjectId == projectId).ToListAsync(); } public async Task AddAsync(string title, int? assigneeId, int projectId, int? creatorId) diff --git a/backend/Repositories/UserRepository.cs b/backend/Repositories/UserRepository.cs index 7d8d11f..a8133aa 100644 --- a/backend/Repositories/UserRepository.cs +++ b/backend/Repositories/UserRepository.cs @@ -13,12 +13,12 @@ public class UserRepository : IRepository public async Task GetAsync() { - return await _dbContext.Users.SingleOrDefaultAsync(x => x.Id == 1); + return await _dbContext.Users.AsNoTracking().SingleOrDefaultAsync(x => x.Id == 1); } public async Task GetAsync(string email) { - return await _dbContext.Users.SingleOrDefaultAsync(x => x.Email == email); + return await _dbContext.Users.AsNoTracking().SingleOrDefaultAsync(x => x.Email == email); } public Task GetAsync(int param1) @@ -26,4 +26,4 @@ public class UserRepository : IRepository throw new NotImplementedException(); } } -} \ No newline at end of file +} From 9c33c7bb6f8ec356b4b36b63fc56873fb0b5fc4a Mon Sep 17 00:00:00 2001 From: kingdomax Date: Wed, 30 Jul 2025 17:44:37 +0200 Subject: [PATCH 13/15] wire up telemetry services with docker (local) --- .../DependencyInjectionConfiguration.cs | 2 +- .../OpenTelemetryConfiguration.cs | 4 +- .../Settings/PostgreSqlSettings.cs | 7 +-- backend/appsettings.json | 7 +-- docker-compose-partial.yml | 45 -------------- docker-compose.yml | 61 +++++++++++++++++++ .../otel-collector-config.yaml | 17 ++++-- observability/prometheus.yml | 7 +++ observability/tempo.yaml | 24 ++++++++ 9 files changed, 108 insertions(+), 66 deletions(-) delete mode 100644 docker-compose-partial.yml rename otel-collector-config.yaml => observability/otel-collector-config.yaml (61%) create mode 100644 observability/prometheus.yml create mode 100644 observability/tempo.yaml diff --git a/backend/Infrastructure/Configurations/DependencyInjectionConfiguration.cs b/backend/Infrastructure/Configurations/DependencyInjectionConfiguration.cs index 03c999a..f2e8122 100644 --- a/backend/Infrastructure/Configurations/DependencyInjectionConfiguration.cs +++ b/backend/Infrastructure/Configurations/DependencyInjectionConfiguration.cs @@ -25,7 +25,7 @@ public static IServiceCollection ConfigureDependencyInjection(this IServiceColle var provider = services.BuildServiceProvider(); services.AddDbContext(options => - options.UseNpgsql(provider.GetRequiredService>().Value.ConnectionString) + options.UseNpgsql(provider.GetRequiredService>().Value.DefaultConnection) ); services.AddSingleton(); diff --git a/backend/Infrastructure/Configurations/OpenTelemetryConfiguration.cs b/backend/Infrastructure/Configurations/OpenTelemetryConfiguration.cs index dd0e7bb..b5dd239 100644 --- a/backend/Infrastructure/Configurations/OpenTelemetryConfiguration.cs +++ b/backend/Infrastructure/Configurations/OpenTelemetryConfiguration.cs @@ -30,7 +30,7 @@ public static void ConfigureTelemetry(this WebApplicationBuilder builder) .AddSqlClientInstrumentation() .AddOtlpExporter(opt => { - opt.Endpoint = new Uri(otelSettings.BaseUrl); // Use Docker service name! and todo-moch: don't hardcode this + opt.Endpoint = new Uri(otelSettings.BaseUrl); // http://otel-collector:4317 (grpc protocol) opt.Protocol = OpenTelemetry.Exporter.OtlpExportProtocol.Grpc; }); }) @@ -43,7 +43,7 @@ public static void ConfigureTelemetry(this WebApplicationBuilder builder) .AddProcessInstrumentation() // CPU usage, Memory, Thread count .AddOtlpExporter(opt => { - opt.Endpoint = new Uri(otelSettings.BaseUrl); // http://otel-collector:4317 + opt.Endpoint = new Uri(otelSettings.BaseUrl); opt.Protocol = OpenTelemetry.Exporter.OtlpExportProtocol.Grpc; }); }); diff --git a/backend/Infrastructure/Settings/PostgreSqlSettings.cs b/backend/Infrastructure/Settings/PostgreSqlSettings.cs index a82c70e..ff49f85 100644 --- a/backend/Infrastructure/Settings/PostgreSqlSettings.cs +++ b/backend/Infrastructure/Settings/PostgreSqlSettings.cs @@ -2,11 +2,6 @@ { public class PostgreSqlSettings { - public required string Host { get; set; } - public required string Port { get; set; } - public required string Database { get; set; } - public required string Username { get; set; } - public required string Password { get; set; } - public string ConnectionString => $"Host={Host};Port={Port};Database={Database};Username={Username};Password={Password}"; + public required string DefaultConnection { get; set; } } } diff --git a/backend/appsettings.json b/backend/appsettings.json index c13d60a..11e697a 100644 --- a/backend/appsettings.json +++ b/backend/appsettings.json @@ -8,12 +8,7 @@ "Urls": ["http://localhost:3039", "http://131.189.90.113:3039"] // dev and production }, "PostgreSql": { - "Host": "localhost", - "Port": "5433", - "Database": "tasksync", - "Username": "postgres", - "Password": "123456789" - // "DefaultConnection": "Host=localhost;Port=5433;Database=tasksync;Username=postgres;Password=123456789" + "DefaultConnection": "Host=localhost;Port=5433;Database=tasksync;Username=postgres;Password=123456789" }, "GamificationApi": { "BaseUrl": "http://localhost:3000" diff --git a/docker-compose-partial.yml b/docker-compose-partial.yml deleted file mode 100644 index d884256..0000000 --- a/docker-compose-partial.yml +++ /dev/null @@ -1,45 +0,0 @@ -services: - postgres: - image: postgres:17 - container_name: tasksync-postgres - restart: on-failure # Restart container automatically if it crashes - ports: - - "5433:5432" - environment: - POSTGRES_USER: postgres - POSTGRES_PASSWORD: 123456789 - POSTGRES_DB: tasksync # Database name to auto-create - volumes: - - pgdata:/var/lib/postgresql/data # Persist Postgres data outside the container - - otel-collector: - image: otel/opentelemetry-collector-contrib:0.97.0 - command: ["--config=/etc/otel-collector-config.yaml"] - volumes: - - ./otel-collector-config.yaml:/etc/otel-collector-config.yaml - ports: - - "4317:4317" # OTLP gRPC - - "4318:4318" # OTLP HTTP - - "8888:8888" # Collector metrics - prometheus: - image: prom/prometheus - ports: ["9090:9090"] - volumes: - - ./prometheus.yml:/etc/prometheus/prometheus.yml - tempo: - image: grafana/tempo:2.4.1 - ports: - - "3200:3200" - loki: - image: grafana/loki:2.9.0 - ports: - - "3100:3100" - grafana: - image: grafana/grafana - ports: - - "3000:3000" - environment: - - GF_SECURITY_ADMIN_PASSWORD=admin - -volumes: - pgdata: # Named volume to store Postgres data (managed by Docker) diff --git a/docker-compose.yml b/docker-compose.yml index 3bff3ec..bbac1b5 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -16,6 +16,7 @@ services: build: context: . # build from project roo dockerfile: backend/Dockerfile # Use Dockerfile in the ./backend directory + profiles: [app] container_name: tasksync-backend restart: on-failure ports: @@ -31,6 +32,7 @@ services: gamification-api: build: context: ./gamification-api + profiles: [app] container_name: tasksync-gamapi restart: on-failure ports: @@ -43,6 +45,7 @@ services: frontend: build: context: ./frontend + profiles: [app] container_name: tasksync-frontend restart: on-failure ports: @@ -51,5 +54,63 @@ services: - backend # Ensure backend is up before serving frontend - gamification-api + otel-collector: + image: otel/opentelemetry-collector-contrib:0.97.0 + profiles: [observability] + container_name: otel-collector + command: ["--config=/etc/otel-collector-config.yaml"] + volumes: + - ./observability/otel-collector-config.yaml:/etc/otel-collector-config.yaml + ports: + - "4317:4317" # OTLP gRPC + - "4318:4318" # OTLP HTTP + - "8889:8889" # Collector internal metrics + + prometheus: + image: prom/prometheus + profiles: [observability] + container_name: prometheus + volumes: + - ./observability/prometheus.yml:/etc/prometheus/prometheus.yml + ports: + - "9090:9090" + depends_on: + - otel-collector + + tempo: + image: grafana/tempo:2.4.1 + profiles: [observability] + container_name: tempo + volumes: + - ./observability/tempo.yaml:/etc/tempo/tempo.yaml + command: ["-config.file=/etc/tempo/tempo.yaml"] + ports: + - "3200:3200" # For grafana, for otelcollector Tempo listens on 4317 for incoming traces by default + depends_on: + - otel-collector + + loki: + image: grafana/loki:2.9.0 + profiles: [observability] + container_name: loki + ports: + - "3100:3100" + depends_on: + - otel-collector + + grafana: + image: grafana/grafana + profiles: [observability] + container_name: grafana + environment: + - GF_SECURITY_ADMIN_PASSWORD=admin + ports: + - "3001:3001" + depends_on: + - prometheus + - tempo + - loki + +# persistent volume volumes: pgdata: # Named volume to store Postgres data (managed by Docker) diff --git a/otel-collector-config.yaml b/observability/otel-collector-config.yaml similarity index 61% rename from otel-collector-config.yaml rename to observability/otel-collector-config.yaml index 527dbea..846df11 100644 --- a/otel-collector-config.yaml +++ b/observability/otel-collector-config.yaml @@ -6,19 +6,24 @@ receivers: exporters: prometheus: - endpoint: "0.0.0.0:8888" - loki: - endpoint: "http://loki:3100/loki/api/v1/push" - tempo: - endpoint: "tempo:4317" + endpoint: "0.0.0.0:8889" #pull model + + otlp/tempo: + endpoint: tempo:4319 #push model tls: insecure: true + loki: + endpoint: "http://loki:3100/loki/api/v1/push" #push model + + logging: + loglevel: info + service: pipelines: traces: receivers: [otlp] - exporters: [tempo] + exporters: [otlp/tempo] metrics: receivers: [otlp] exporters: [prometheus] diff --git a/observability/prometheus.yml b/observability/prometheus.yml new file mode 100644 index 0000000..144bdea --- /dev/null +++ b/observability/prometheus.yml @@ -0,0 +1,7 @@ +global: + scrape_interval: 5s + +scrape_configs: + - job_name: "otel-collector" + static_configs: + - targets: ["otel-collector:8889"] diff --git a/observability/tempo.yaml b/observability/tempo.yaml new file mode 100644 index 0000000..1a0ecf1 --- /dev/null +++ b/observability/tempo.yaml @@ -0,0 +1,24 @@ +server: + http_listen_port: 3200 + grpc_listen_port: 4319 + +distributor: + receivers: + otlp: + protocols: + grpc: + http: + +ingester: + trace_idle_period: 10s + max_block_bytes: 1_000_000 + +compactor: + compaction: + block_retention: 48h + +storage: + trace: + backend: local + local: + path: /var/tempo/traces From 8e3a98a792cebd38b7096d84039907376f990704 Mon Sep 17 00:00:00 2001 From: kingdomax Date: Wed, 30 Jul 2025 18:05:04 +0200 Subject: [PATCH 14/15] change Grafana port --- docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index bbac1b5..f3222fe 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -105,7 +105,7 @@ services: environment: - GF_SECURITY_ADMIN_PASSWORD=admin ports: - - "3001:3001" + - "3400:3000" depends_on: - prometheus - tempo From 581713f85cd44771c5435e417ffdf9d6d0727fa0 Mon Sep 17 00:00:00 2001 From: kingdomax Date: Thu, 31 Jul 2025 17:24:16 +0200 Subject: [PATCH 15/15] forgot to configure httpclient in program.cs --- backend/ExternalApi/GamificationApi.cs | 2 +- backend/Program.cs | 6 +++++- backend/appsettings.Development.json | 5 +++-- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/backend/ExternalApi/GamificationApi.cs b/backend/ExternalApi/GamificationApi.cs index 2947425..3519bba 100644 --- a/backend/ExternalApi/GamificationApi.cs +++ b/backend/ExternalApi/GamificationApi.cs @@ -23,7 +23,7 @@ public async Task UpdatePoint(int taskId, TaskStatus status) { try { - var httpMessage = new HttpRequestMessage(HttpMethod.Post, $"{_httpClient.BaseAddress}/points") + var httpMessage = new HttpRequestMessage(HttpMethod.Post, "points") { Content = JsonContent.Create(new CreatePointDto() { diff --git a/backend/Program.cs b/backend/Program.cs index 5a906c1..fa5745b 100644 --- a/backend/Program.cs +++ b/backend/Program.cs @@ -18,10 +18,14 @@ builder.Services.ConfigureAppSettings(builder.Configuration); builder.Services.ConfigureDependencyInjection(); builder.Services.ConfigureResponseCompression(); +builder.Services.ConfigureHttpClient(); builder.Services.ConfigureApiVersion(); builder.Services.ConfigureCors(); builder.Services.ConfigureJwt(); -builder.ConfigureTelemetry(); +if (builder.Environment.IsDevelopment()) +{ + builder.ConfigureTelemetry(); +} // ------------------------------------------------------------------------------- // -------------- Configure/Order the request pipeline (Middleware) -------------- diff --git a/backend/appsettings.Development.json b/backend/appsettings.Development.json index e1565b8..48b6267 100644 --- a/backend/appsettings.Development.json +++ b/backend/appsettings.Development.json @@ -1,12 +1,13 @@ { "AppInfo": { - "AppName": "TaskSync Core API", + "AppName": "Core.API", "Environment": "Development" }, "Logging": { "LogLevel": { "Default": "Information", - "Microsoft.AspNetCore": "Warning" + "Microsoft.AspNetCore": "Warning", + "Microsoft.EntityFrameworkCore.Database.Command": "Warning" } } }