Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -153,4 +153,7 @@ codeql-results.csv
security-scan-results.csv

# MemoryLens MCP fork
memorylens-mcp/
memorylens-mcp/

# Profiling snapshots
profiling/snapshots/
91 changes: 89 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# ServerEye Web - Environment Management Makefile
# Enterprise-level environment management with component separation

.PHONY: help dev-up dev-down dev-logs dev-clean build test lint dev-infra-up dev-observability-up dev-backend-up dev-frontend-up dev-stripe-up dev-infra-down dev-observability-down dev-backend-down dev-frontend-down dev-stripe-down dev-infra-logs dev-observability-logs dev-backend-logs dev-frontend-logs dev-stripe-logs dev-shell
.PHONY: help dev-up dev-down dev-logs dev-clean build test lint dev-infra-up dev-observability-up dev-backend-up dev-frontend-up dev-stripe-up dev-infra-down dev-observability-down dev-backend-down dev-frontend-down dev-stripe-down dev-infra-logs dev-observability-logs dev-backend-logs dev-frontend-logs dev-stripe-logs dev-shell prod-up prod-down prod-logs prod-clean prod-infra-up prod-observability-up prod-backend-up prod-frontend-up prod-stripe-up prof-memory prof-cpu

# Default target
help:
Expand Down Expand Up @@ -31,7 +31,13 @@ help:
@echo " dev-backend-logs - Show backend logs"
@echo " dev-frontend-logs - Show frontend logs"
@echo ""
@echo "� Build & Test:"
@echo "🚀 Production - Full Stack:"
@echo " prod-up - Start all production services (infra + stripe + observability + backend + frontend)"
@echo " prod-down - Stop all production services"
@echo " prod-logs - Show all production logs"
@echo " prod-clean - Clean all production services"
@echo ""
@echo "🏗️ Build & Test:"
@echo " build - Build all development services"
@echo " test - Run all tests (including Docker tests)"
@echo " test-backend - Run all backend tests"
Expand All @@ -43,6 +49,10 @@ help:
@echo " clean - Clean all environments"
@echo " status - Show status of all services"
@echo " backup - Backup databases"
@echo ""
@echo "🔍 Profiling:"
@echo " prof-memory - Create memory snapshot with manual load testing (60s)"
@echo " prof-cpu - Create CPU profiling snapshot with dotTrace (60s)"

# ==============================================================================
# DEVELOPMENT ENVIRONMENT
Expand Down Expand Up @@ -165,6 +175,71 @@ dev-shell:
@echo "🐚 Accessing development backend shell..."
docker exec -it servereye-backend-dev /bin/sh

# ==============================================================================
# PRODUCTION ENVIRONMENT
# ==============================================================================

# Full Stack Production
prod-up: prod-infra-up prod-stripe-up prod-observability-up prod-backend-up prod-frontend-up
@echo "All production services started!"
@echo "Frontend: http://127.0.0.1:3001"
@echo "Backend: http://127.0.0.1:5248"
@echo "PostgreSQL: 127.0.0.1:5435 (main), 127.0.0.1:5437 (tickets), 127.0.0.1:5438 (billing)"
@echo "Redis: 127.0.0.1:6381"
@echo "Grafana: http://localhost:3011"
@echo "Prometheus: http://localhost:9091"

prod-down: prod-frontend-down prod-backend-down prod-stripe-down prod-observability-down prod-infra-down
@echo "✅ All production services stopped!"

prod-logs:
@echo "Showing all production logs..."
docker compose -f ./environments/prod/infrastructure/docker-compose.yml logs -f & \
docker compose -f ./environments/prod/stripe/docker-compose.yml logs -f & \
docker compose -f ./environments/prod/observability/docker-compose.yml logs -f & \
docker compose -f ./environments/prod/backend/docker-compose.yml logs -f & \
docker compose -f ./environments/prod/frontend/docker-compose.yml logs -f

prod-clean: prod-down
@echo "Cleaning production environment..."
docker compose -f ./environments/prod/infrastructure/docker-compose.yml down -v --remove-orphans
docker compose -f ./environments/prod/stripe/docker-compose.yml down -v --remove-orphans
docker compose -f ./environments/prod/observability/docker-compose.yml down -v --remove-orphans
docker compose -f ./environments/prod/backend/docker-compose.yml down -v --remove-orphans
docker compose -f ./environments/prod/frontend/docker-compose.yml down -v --remove-orphans
@echo "Production environment cleaned!"

# Component-wise Production Commands
prod-infra-up:
@echo "🏗️ Starting production infrastructure..."
@docker network create servereye-network 2>/dev/null || echo "✅ Network servereye-network already exists"
cd ./environments/prod && doppler run -- docker compose -f ./infrastructure/docker-compose.yml up -d
@echo "✅ Infrastructure started!"

prod-observability-up:
@echo "📊 Starting production observability stack..."
@docker network create servereye-network 2>/dev/null || echo "✅ Network servereye-network already exists"
cd ./environments/prod && doppler run -- docker compose -f ./observability/docker-compose.yml up -d
@echo "✅ Observability stack started!"

prod-backend-up:
@echo "🔧 Starting production backend..."
@docker network create servereye-network 2>/dev/null || echo "✅ Network servereye-network already exists"
cd ./environments/prod && doppler run -- docker compose -f ./backend/docker-compose.yml up -d --build
@echo "✅ Backend started!"

prod-frontend-up:
@echo "🌐 Starting production frontend..."
@docker network create servereye-network 2>/dev/null || echo "✅ Network servereye-network already exists"
cd ./environments/prod && doppler run -- docker compose -f ./frontend/docker-compose.yml up -d --build
@echo "✅ Frontend started!"

prod-stripe-up:
@echo "Starting Stripe CLI for production webhook forwarding..."
@docker network create servereye-network 2>/dev/null || echo "✅ Network servereye-network already exists"
cd ./environments/prod && doppler run -- docker compose -f ./stripe/docker-compose.yml up -d
@echo "✅ Stripe CLI started!"

# ==============================================================================
# BUILD COMMANDS
# ==============================================================================
Expand Down Expand Up @@ -279,6 +354,18 @@ restore:
docker exec -i servereyeWeb-ticket-postgres psql -U postgres ServerEyeWeb_Dev_Ticket < "$$ticket_backup"
@echo "✅ Databases restored!"

# ==============================================================================
# PROFILING COMMANDS
# ==============================================================================

prof-memory:
@echo "🔍 Starting memory profiling..."
@./profiling/memory/memory-snapshot-auto-load.sh

prof-cpu:
@echo "🔍 Starting CPU profiling with dotTrace..."
@./profiling/dottrace/prof-cpu.sh

# ==============================================================================
# DEVELOPMENT HELPERS
# ==============================================================================
Expand Down
54 changes: 27 additions & 27 deletions backend/ServerEyeBackend/Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -4,57 +4,57 @@
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="AWSSDK.Extensions.NETCore.Setup" Version="4.0.3.33" />
<PackageVersion Include="AWSSDK.SimpleEmail" Version="4.0.2.24" />
<PackageVersion Include="Microsoft.EntityFrameworkCore" Version="10.0.5" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Design" Version="10.0.5" />
<PackageVersion Include="AWSSDK.SimpleEmail" Version="4.0.2.27" />
<PackageVersion Include="Microsoft.EntityFrameworkCore" Version="10.0.7" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Design" Version="10.0.7" />
<PackageVersion Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="10.0.1" />
<PackageVersion Include="Stripe.net" Version="51.0.0" />
<PackageVersion Include="Stripe.net" Version="51.1.0" />
<PackageVersion Include="StyleCop.Analyzers" Version="1.2.0-beta.556">
</PackageVersion>
<PackageVersion Include="Microsoft.AspNetCore.OpenApi" Version="10.0.5">
<PackageVersion Include="Microsoft.AspNetCore.OpenApi" Version="10.0.7">
</PackageVersion>
<PackageVersion Include="BCrypt.Net-Next" Version="4.1.0" />
<PackageVersion Include="FluentValidation" Version="11.11.0" />
<PackageVersion Include="FluentValidation.DependencyInjectionExtensions" Version="12.1.1" />
<PackageVersion Include="Microsoft.Extensions.Configuration.Binder" Version="10.0.5" />
<PackageVersion Include="Microsoft.Extensions.Caching.Abstractions" Version="10.0.5" />
<PackageVersion Include="System.IdentityModel.Tokens.Jwt" Version="8.17.0" />
<PackageVersion Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="10.0.5" />
<PackageVersion Include="Microsoft.Extensions.Configuration" Version="10.0.5" />
<PackageVersion Include="Microsoft.Extensions.Configuration.Json" Version="10.0.5" />
<PackageVersion Include="Microsoft.Extensions.Configuration.Binder" Version="10.0.7" />
<PackageVersion Include="Microsoft.Extensions.Caching.Abstractions" Version="10.0.7" />
<PackageVersion Include="System.IdentityModel.Tokens.Jwt" Version="8.18.0" />
<PackageVersion Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="10.0.7" />
<PackageVersion Include="Microsoft.Extensions.Configuration" Version="10.0.7" />
<PackageVersion Include="Microsoft.Extensions.Configuration.Json" Version="10.0.7" />
<PackageVersion Include="StackExchange.Redis" Version="2.12.14" />
<PackageVersion Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="10.0.5" />
<PackageVersion Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="10.0.7" />
<PackageVersion Include="AspNetCore.HealthChecks.NpgSql" Version="9.0.0" />
<PackageVersion Include="AspNetCore.HealthChecks.Redis" Version="9.0.0" />
<PackageVersion Include="Moq" Version="4.20.72" />
<PackageVersion Include="FluentAssertions" Version="8.9.0" />
<PackageVersion Include="NSubstitute" Version="5.3.0" />
<PackageVersion Include="AutoFixture" Version="4.18.1" />
<PackageVersion Include="AutoFixture.Xunit2" Version="4.18.1" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.InMemory" Version="10.0.5" />
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Testing" Version="10.0.5" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.InMemory" Version="10.0.7" />
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Testing" Version="10.0.7" />
<PackageVersion Include="Testcontainers" Version="4.11.0" />
<PackageVersion Include="Testcontainers.PostgreSql" Version="4.11.0" />
<PackageVersion Include="coverlet.collector" Version="8.0.1" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.4.0" />
<PackageVersion Include="coverlet.collector" Version="10.0.0" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.5.1" />
<PackageVersion Include="xunit" Version="2.9.3" />
<PackageVersion Include="xunit.runner.visualstudio" Version="3.1.5" />
<PackageVersion Include="Microsoft.AspNetCore.Authentication.Google" Version="10.0.5" />
<PackageVersion Include="Microsoft.AspNetCore.Authentication.MicrosoftAccount" Version="10.0.5" />
<PackageVersion Include="Microsoft.AspNetCore.Authentication.Google" Version="10.0.7" />
<PackageVersion Include="Microsoft.AspNetCore.Authentication.MicrosoftAccount" Version="10.0.7" />
<PackageVersion Include="Microsoft.AspNetCore.Authentication.OAuth" Version="2.3.9" />
<PackageVersion Include="System.Text.Json" Version="10.0.2" />
<PackageVersion Include="OpenTelemetry" Version="1.15.2" />
<PackageVersion Include="OpenTelemetry.Extensions.Hosting" Version="1.15.2" />
<PackageVersion Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.15.1" />
<PackageVersion Include="OpenTelemetry.Instrumentation.Http" Version="1.15.0" />
<PackageVersion Include="OpenTelemetry" Version="1.15.3" />
<PackageVersion Include="OpenTelemetry.Extensions.Hosting" Version="1.15.3" />
<PackageVersion Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.15.2" />
<PackageVersion Include="OpenTelemetry.Instrumentation.Http" Version="1.15.1" />
<PackageVersion Include="OpenTelemetry.Instrumentation.EntityFrameworkCore" Version="1.0.0-beta.12" />
<PackageVersion Include="OpenTelemetry.Instrumentation.StackExchangeRedis" Version="1.0.0-rc9.15" />
<PackageVersion Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.15.2" />
<PackageVersion Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.15.3" />
<PackageVersion Include="OpenTelemetry.Exporter.Prometheus.AspNetCore" Version="1.9.0-beta.2" />
<PackageVersion Include="OpenTelemetry.Exporter.InMemory" Version="1.15.2" />
<PackageVersion Include="Microsoft.Extensions.Diagnostics.HealthChecks" Version="10.0.5" />
<PackageVersion Include="Microsoft.Extensions.Hosting.Abstractions" Version="10.0.5" />
<PackageVersion Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="10.0.5" />
<PackageVersion Include="OpenTelemetry.Exporter.InMemory" Version="1.15.3" />
<PackageVersion Include="Microsoft.Extensions.Diagnostics.HealthChecks" Version="10.0.7" />
<PackageVersion Include="Microsoft.Extensions.Hosting.Abstractions" Version="10.0.7" />
<PackageVersion Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="10.0.7" />
<PackageVersion Include="Npgsql" Version="10.0.2" />
<PackageVersion Include="System.Diagnostics.DiagnosticSource" Version="10.0.2" />
</ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,14 @@ public static IServiceCollection AddAuthenticationConfiguration(
{
// Load RSA key from JwtSettings
RSA rsaKey;
if (!string.IsNullOrEmpty(jwtSettings.PrivateKeyBase64) && !string.IsNullOrEmpty(jwtSettings.PublicKeyBase64))
if (!string.IsNullOrEmpty(jwtSettings.PrivateKey) && !string.IsNullOrEmpty(jwtSettings.PublicKey))
{
rsaKey = LoadRsaKeyFromBase64(jwtSettings.PublicKeyBase64);
rsaKey = LoadRsaKeyFromBase64(jwtSettings.PublicKey);
}
else
{
throw new InvalidOperationException(
"JWT PrivateKeyBase64 and PublicKeyBase64 must be configured. " +
"JWT PrivateKey and PublicKey must be configured. " +
"Please add them to Doppler dev config.");
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,24 @@ public static IServiceCollection AddDatabaseConfiguration(
return services;
}

// Register DbContexts
services.AddDbContext<ServerEyeDbContext>(options =>
options.UseNpgsql(serverEyeConnectionString));
// Register DbContexts with pooling for better performance and reduced memory allocations
services.AddDbContextPool<ServerEyeDbContext>(options =>
{
options.UseNpgsql(serverEyeConnectionString);
options.ConfigureWarnings(warnings => warnings.Default(WarningBehavior.Ignore));
});

services.AddDbContext<TicketDbContext>(options =>
options.UseNpgsql(ticketConnectionString));
services.AddDbContextPool<TicketDbContext>(options =>
{
options.UseNpgsql(ticketConnectionString);
options.ConfigureWarnings(warnings => warnings.Default(WarningBehavior.Ignore));
});

services.AddDbContext<BillingDbContext>(options =>
options.UseNpgsql(billingConnectionString));
services.AddDbContextPool<BillingDbContext>(options =>
{
options.UseNpgsql(billingConnectionString);
options.ConfigureWarnings(warnings => warnings.Default(WarningBehavior.Ignore));
});

// Add Health Checks for all databases
services.AddHealthChecks()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,11 +151,11 @@ private static void RegisterAuthServices(IServiceCollection services)

// Log JWT settings for debugging
logger?.LogInformation(
"Registering JwtService - PrivateKeyBase64 length: {Length}, PublicKeyBase64 length: {Length}",
jwtSettings.PrivateKeyBase64?.Length ?? 0,
jwtSettings.PublicKeyBase64?.Length ?? 0);
"Registering JwtService - PrivateKey length: {Length}, PublicKey length: {Length}",
jwtSettings.PrivateKey?.Length ?? 0,
jwtSettings.PublicKey?.Length ?? 0);

return new JwtService(jwtSettings, configuration, logger);
return new JwtService(jwtSettings, logger);
});
}

Expand Down Expand Up @@ -294,11 +294,15 @@ private static void AddHealthChecks(IServiceCollection services, IConfiguration
.AddCheck<DatabaseHealthCheckFactory<Infrastructure.ServerEyeDbContext>>("main_database", tags: DatabaseTags)
.AddCheck<DatabaseHealthCheckFactory<Infrastructure.TicketDbContext>>("ticket_database", tags: DatabaseTags);

// Register factory instances with database names
// Register factory instances with database names using IServiceScopeFactory
services.AddSingleton(provider =>
new DatabaseHealthCheckFactory<Infrastructure.ServerEyeDbContext>(provider, "Main Database"));
new DatabaseHealthCheckFactory<Infrastructure.ServerEyeDbContext>(
provider.GetRequiredService<IServiceScopeFactory>(),
"Main Database"));
services.AddSingleton(provider =>
new DatabaseHealthCheckFactory<Infrastructure.TicketDbContext>(provider, "Ticket Database"));
new DatabaseHealthCheckFactory<Infrastructure.TicketDbContext>(
provider.GetRequiredService<IServiceScopeFactory>(),
"Ticket Database"));

// Add Redis health check if configured
var redisConnectionString = configuration.GetConnectionString("Redis");
Expand Down
Loading
Loading