From a72ffc3f14fa60fadf9a5a43c17046af94da1c7d Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sun, 23 Nov 2025 16:13:06 +0000
Subject: [PATCH 1/6] Initial plan
From 3543eef41f1c897c8931d52a14d890e9ac07e8b6 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sun, 23 Nov 2025 16:20:04 +0000
Subject: [PATCH 2/6] Add .NET 10, central package management, and VS Code
debug config
Co-authored-by: punkouter26 <121304072+punkouter26@users.noreply.github.com>
---
.gitignore | 2 +
Directory.Packages.props | 65 ++++++++++++++++++++++
PoFastType.Api/PoFastType.Api.csproj | 41 +++++++-------
PoFastType.Client/PoFastType.Client.csproj | 13 +++--
PoFastType.Shared/PoFastType.Shared.csproj | 5 +-
PoFastType.Tests/PoFastType.Tests.csproj | 26 +++++----
global.json | 6 ++
7 files changed, 118 insertions(+), 40 deletions(-)
create mode 100644 Directory.Packages.props
create mode 100644 global.json
diff --git a/.gitignore b/.gitignore
index 149cb9a..1229b63 100644
--- a/.gitignore
+++ b/.gitignore
@@ -145,6 +145,8 @@ temp/
# Editor directories and files
.vscode/*
+!.vscode/launch.json
+!.vscode/tasks.json
!.vscode/extensions.json
.idea
.DS_Store
diff --git a/Directory.Packages.props b/Directory.Packages.props
new file mode 100644
index 0000000..cbe75a9
--- /dev/null
+++ b/Directory.Packages.props
@@ -0,0 +1,65 @@
+
+
+ true
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/PoFastType.Api/PoFastType.Api.csproj b/PoFastType.Api/PoFastType.Api.csproj
index 14a77bf..dcd2524 100644
--- a/PoFastType.Api/PoFastType.Api.csproj
+++ b/PoFastType.Api/PoFastType.Api.csproj
@@ -1,31 +1,32 @@
๏ปฟ
- net9.0
+ net10.0
enable
enable
6226cd99-6765-4cd7-8191-b8247a1390fc
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- all
- runtime; build; native; contentfiles; analyzers; buildtransitive
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/PoFastType.Client/PoFastType.Client.csproj b/PoFastType.Client/PoFastType.Client.csproj
index 2253aa3..e9400a7 100644
--- a/PoFastType.Client/PoFastType.Client.csproj
+++ b/PoFastType.Client/PoFastType.Client.csproj
@@ -1,17 +1,18 @@
๏ปฟ
- net9.0
+ net10.0
enable
enable
40e08d54-7033-46bf-a611-aef34e82c645
+
-
-
-
-
-
+
+
+
+
+
diff --git a/PoFastType.Shared/PoFastType.Shared.csproj b/PoFastType.Shared/PoFastType.Shared.csproj
index d6f0cd2..9367463 100644
--- a/PoFastType.Shared/PoFastType.Shared.csproj
+++ b/PoFastType.Shared/PoFastType.Shared.csproj
@@ -1,13 +1,14 @@
๏ปฟ
- net9.0
+ net10.0
enable
enable
-
+
+
diff --git a/PoFastType.Tests/PoFastType.Tests.csproj b/PoFastType.Tests/PoFastType.Tests.csproj
index c05447d..b069fae 100644
--- a/PoFastType.Tests/PoFastType.Tests.csproj
+++ b/PoFastType.Tests/PoFastType.Tests.csproj
@@ -1,7 +1,7 @@
๏ปฟ
- net9.0
+ net10.0
enable
enable
false
@@ -9,23 +9,25 @@
-
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
-
-
-
+
+
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
-
-
-
-
-
+
+
+
+
+
+
+
+
diff --git a/global.json b/global.json
new file mode 100644
index 0000000..b24aad6
--- /dev/null
+++ b/global.json
@@ -0,0 +1,6 @@
+{
+ "sdk": {
+ "version": "10.0.100",
+ "rollForward": "latestPatch"
+ }
+}
From 9ac3083a1f19816749299dce70c5e06b492ca95f Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sun, 23 Nov 2025 16:24:29 +0000
Subject: [PATCH 3/6] Reorganize to standard folder structure (/src, /tests,
/docs, /scripts)
Co-authored-by: punkouter26 <121304072+punkouter26@users.noreply.github.com>
---
PoFastType.sln | 8 +-
README.md | 137 ++++++++++--------
.../Diagrams}/SIMPLE_class-diagram.mmd | 0
.../Diagrams}/SIMPLE_class-diagram.svg | 0
.../Diagrams}/SIMPLE_component-hierarchy.mmd | 0
.../Diagrams}/SIMPLE_component-hierarchy.svg | 0
.../Diagrams}/SIMPLE_flowchart.mmd | 0
.../Diagrams}/SIMPLE_flowchart.svg | 0
.../Diagrams}/SIMPLE_project-dependency.mmd | 0
.../Diagrams}/SIMPLE_project-dependency.svg | 0
.../Diagrams}/SIMPLE_sequence-diagram.mmd | 0
.../Diagrams}/SIMPLE_sequence-diagram.svg | 0
{Diagrams => docs/Diagrams}/class-diagram.mmd | 0
{Diagrams => docs/Diagrams}/class-diagram.svg | 0
.../Diagrams}/component-hierarchy.mmd | 0
.../Diagrams}/component-hierarchy.svg | 0
{Diagrams => docs/Diagrams}/flowchart.mmd | 0
{Diagrams => docs/Diagrams}/flowchart.svg | 0
.../Diagrams}/project-dependency.mmd | 0
.../Diagrams}/project-dependency.svg | 0
.../Diagrams}/sequence-diagram.mmd | 0
.../Diagrams}/sequence-diagram.svg | 0
PRD.md => docs/PRD.md | 0
docs/README.md | 28 ++++
docs/kql/app-performance.kql | 12 ++
docs/kql/endpoint-performance.kql | 13 ++
docs/kql/error-analysis.kql | 10 ++
docs/kql/storage-health.kql | 12 ++
docs/kql/top-scores.kql | 14 ++
docs/kql/user-activity.kql | 12 ++
scripts/run-coverage.ps1 | 33 +++++
scripts/run-coverage.sh | 30 ++++
scripts/start-azurite.ps1 | 23 +++
scripts/start-azurite.sh | 22 +++
.../Controllers/DiagController.cs | 0
.../Controllers/GameController.cs | 0
.../Controllers/ScoresController.cs | 0
.../Controllers/UserController.cs | 0
.../AzureTableStorageHealthCheck.cs | 0
.../Middleware/GlobalExceptionMiddleware.cs | 0
.../PoFastType.Api}/PoFastType.Api.csproj | 0
.../PoFastType.Api}/Program.cs | 0
.../PoFastType - Web Deploy/profile.arm.json | 0
.../Properties/launchSettings.json | 0
.../PoFastType.Api}/README.md | 0
.../AzureTableGameResultRepository.cs | 0
.../Repositories/IGameResultRepository.cs | 0
.../PoFastType.Api}/Services/GameService.cs | 0
.../Services/HardcodedTextStrategy.cs | 0
.../PoFastType.Api}/Services/IGameService.cs | 0
.../Services/ITextGenerationService.cs | 0
.../Services/ITextGenerationStrategy.cs | 0
.../Services/IUserIdentityService.cs | 0
.../Services/TextGenerationService.cs | 0
.../Services/UserIdentityService.cs | 0
.../appsettings.Development.json | 0
.../appsettings.Production.json | 0
.../PoFastType.Api}/appsettings.json | 0
.../PoFastType.Api}/log.txt | 0
.../PoFastType.Api}/web.config | 0
.../PoFastType.Client}/App.razor | 0
.../Components/ErrorBoundary.razor | 0
.../Components/Navbar_New.razor | 0
.../Layout/MainLayout.razor | 0
.../PoFastType.Client}/Pages/Diag.razor | 0
.../PoFastType.Client}/Pages/Home.razor | 0
.../Pages/Leaderboard.razor | 0
.../PoFastType.Client}/Pages/UserStats.razor | 0
.../PoFastType.Client.csproj | 0
.../PoFastType.Client}/Program.cs | 0
.../Properties/launchSettings.json | 0
.../PoFastType.Client}/README.md | 0
.../Services/GameStateService.cs | 0
.../Services/IUserService.cs | 0
.../Services/UserService.cs | 0
.../PoFastType.Client}/_Imports.razor | 0
.../PoFastType.Client}/wwwroot/app.js | 0
.../wwwroot/appsettings.json | 0
.../wwwroot/css/retro-arcade.css | 0
.../PoFastType.Client}/wwwroot/favicon.png | Bin
.../PoFastType.Client}/wwwroot/icon-192.png | Bin
.../PoFastType.Client}/wwwroot/index.html | 0
.../wwwroot/sample-data/weather.json | 0
.../PoFastType.Shared}/Models/GameResult.cs | 0
.../Models/LeaderboardEntry.cs | 0
.../Models/UserGameResult.cs | 0
.../PoFastType.Shared}/Models/UserIdentity.cs | 0
.../PoFastType.Shared.csproj | 0
.../PoFastType.Shared}/README.md | 0
.../API/Controllers/DiagControllerTests.cs | 0
.../API/Controllers/GameControllerTests.cs | 0
.../API/Controllers/ScoresControllerTests.cs | 0
.../PoFastType.Tests}/E2E/HomePageE2ETests.cs | 0
.../E2E/LeaderboardPageE2ETests.cs | 0
.../E2E/ResponsiveDesignE2ETests.cs | 0
.../E2E/UserStatsPageE2ETests.cs | 0
.../AzureTableStorageIntegrationTests.cs | 0
.../Integration/Services/GameServiceTests.cs | 0
.../PoFastType.Tests}/PoFastType.Tests.csproj | 4 +-
.../PoFastType.Tests}/README.md | 0
.../System/TypingGameSystemTests.cs | 0
.../CustomWebApplicationFactory.cs | 0
.../AzureTableGameResultRepositoryTests.cs | 0
.../Services/HardcodedTextStrategyTests.cs | 0
.../Services/TextGenerationServiceTests.cs | 0
105 files changed, 293 insertions(+), 65 deletions(-)
rename {Diagrams => docs/Diagrams}/SIMPLE_class-diagram.mmd (100%)
rename {Diagrams => docs/Diagrams}/SIMPLE_class-diagram.svg (100%)
rename {Diagrams => docs/Diagrams}/SIMPLE_component-hierarchy.mmd (100%)
rename {Diagrams => docs/Diagrams}/SIMPLE_component-hierarchy.svg (100%)
rename {Diagrams => docs/Diagrams}/SIMPLE_flowchart.mmd (100%)
rename {Diagrams => docs/Diagrams}/SIMPLE_flowchart.svg (100%)
rename {Diagrams => docs/Diagrams}/SIMPLE_project-dependency.mmd (100%)
rename {Diagrams => docs/Diagrams}/SIMPLE_project-dependency.svg (100%)
rename {Diagrams => docs/Diagrams}/SIMPLE_sequence-diagram.mmd (100%)
rename {Diagrams => docs/Diagrams}/SIMPLE_sequence-diagram.svg (100%)
rename {Diagrams => docs/Diagrams}/class-diagram.mmd (100%)
rename {Diagrams => docs/Diagrams}/class-diagram.svg (100%)
rename {Diagrams => docs/Diagrams}/component-hierarchy.mmd (100%)
rename {Diagrams => docs/Diagrams}/component-hierarchy.svg (100%)
rename {Diagrams => docs/Diagrams}/flowchart.mmd (100%)
rename {Diagrams => docs/Diagrams}/flowchart.svg (100%)
rename {Diagrams => docs/Diagrams}/project-dependency.mmd (100%)
rename {Diagrams => docs/Diagrams}/project-dependency.svg (100%)
rename {Diagrams => docs/Diagrams}/sequence-diagram.mmd (100%)
rename {Diagrams => docs/Diagrams}/sequence-diagram.svg (100%)
rename PRD.md => docs/PRD.md (100%)
create mode 100644 docs/README.md
create mode 100644 docs/kql/app-performance.kql
create mode 100644 docs/kql/endpoint-performance.kql
create mode 100644 docs/kql/error-analysis.kql
create mode 100644 docs/kql/storage-health.kql
create mode 100644 docs/kql/top-scores.kql
create mode 100644 docs/kql/user-activity.kql
create mode 100644 scripts/run-coverage.ps1
create mode 100755 scripts/run-coverage.sh
create mode 100644 scripts/start-azurite.ps1
create mode 100755 scripts/start-azurite.sh
rename {PoFastType.Api => src/PoFastType.Api}/Controllers/DiagController.cs (100%)
rename {PoFastType.Api => src/PoFastType.Api}/Controllers/GameController.cs (100%)
rename {PoFastType.Api => src/PoFastType.Api}/Controllers/ScoresController.cs (100%)
rename {PoFastType.Api => src/PoFastType.Api}/Controllers/UserController.cs (100%)
rename {PoFastType.Api => src/PoFastType.Api}/HealthChecks/AzureTableStorageHealthCheck.cs (100%)
rename {PoFastType.Api => src/PoFastType.Api}/Middleware/GlobalExceptionMiddleware.cs (100%)
rename {PoFastType.Api => src/PoFastType.Api}/PoFastType.Api.csproj (100%)
rename {PoFastType.Api => src/PoFastType.Api}/Program.cs (100%)
rename {PoFastType.Api => src/PoFastType.Api}/Properties/ServiceDependencies/PoFastType - Web Deploy/profile.arm.json (100%)
rename {PoFastType.Api => src/PoFastType.Api}/Properties/launchSettings.json (100%)
rename {PoFastType.Api => src/PoFastType.Api}/README.md (100%)
rename {PoFastType.Api => src/PoFastType.Api}/Repositories/AzureTableGameResultRepository.cs (100%)
rename {PoFastType.Api => src/PoFastType.Api}/Repositories/IGameResultRepository.cs (100%)
rename {PoFastType.Api => src/PoFastType.Api}/Services/GameService.cs (100%)
rename {PoFastType.Api => src/PoFastType.Api}/Services/HardcodedTextStrategy.cs (100%)
rename {PoFastType.Api => src/PoFastType.Api}/Services/IGameService.cs (100%)
rename {PoFastType.Api => src/PoFastType.Api}/Services/ITextGenerationService.cs (100%)
rename {PoFastType.Api => src/PoFastType.Api}/Services/ITextGenerationStrategy.cs (100%)
rename {PoFastType.Api => src/PoFastType.Api}/Services/IUserIdentityService.cs (100%)
rename {PoFastType.Api => src/PoFastType.Api}/Services/TextGenerationService.cs (100%)
rename {PoFastType.Api => src/PoFastType.Api}/Services/UserIdentityService.cs (100%)
rename {PoFastType.Api => src/PoFastType.Api}/appsettings.Development.json (100%)
rename {PoFastType.Api => src/PoFastType.Api}/appsettings.Production.json (100%)
rename {PoFastType.Api => src/PoFastType.Api}/appsettings.json (100%)
rename {PoFastType.Api => src/PoFastType.Api}/log.txt (100%)
rename {PoFastType.Api => src/PoFastType.Api}/web.config (100%)
rename {PoFastType.Client => src/PoFastType.Client}/App.razor (100%)
rename {PoFastType.Client => src/PoFastType.Client}/Components/ErrorBoundary.razor (100%)
rename {PoFastType.Client => src/PoFastType.Client}/Components/Navbar_New.razor (100%)
rename {PoFastType.Client => src/PoFastType.Client}/Layout/MainLayout.razor (100%)
rename {PoFastType.Client => src/PoFastType.Client}/Pages/Diag.razor (100%)
rename {PoFastType.Client => src/PoFastType.Client}/Pages/Home.razor (100%)
rename {PoFastType.Client => src/PoFastType.Client}/Pages/Leaderboard.razor (100%)
rename {PoFastType.Client => src/PoFastType.Client}/Pages/UserStats.razor (100%)
rename {PoFastType.Client => src/PoFastType.Client}/PoFastType.Client.csproj (100%)
rename {PoFastType.Client => src/PoFastType.Client}/Program.cs (100%)
rename {PoFastType.Client => src/PoFastType.Client}/Properties/launchSettings.json (100%)
rename {PoFastType.Client => src/PoFastType.Client}/README.md (100%)
rename {PoFastType.Client => src/PoFastType.Client}/Services/GameStateService.cs (100%)
rename {PoFastType.Client => src/PoFastType.Client}/Services/IUserService.cs (100%)
rename {PoFastType.Client => src/PoFastType.Client}/Services/UserService.cs (100%)
rename {PoFastType.Client => src/PoFastType.Client}/_Imports.razor (100%)
rename {PoFastType.Client => src/PoFastType.Client}/wwwroot/app.js (100%)
rename {PoFastType.Client => src/PoFastType.Client}/wwwroot/appsettings.json (100%)
rename {PoFastType.Client => src/PoFastType.Client}/wwwroot/css/retro-arcade.css (100%)
rename {PoFastType.Client => src/PoFastType.Client}/wwwroot/favicon.png (100%)
rename {PoFastType.Client => src/PoFastType.Client}/wwwroot/icon-192.png (100%)
rename {PoFastType.Client => src/PoFastType.Client}/wwwroot/index.html (100%)
rename {PoFastType.Client => src/PoFastType.Client}/wwwroot/sample-data/weather.json (100%)
rename {PoFastType.Shared => src/PoFastType.Shared}/Models/GameResult.cs (100%)
rename {PoFastType.Shared => src/PoFastType.Shared}/Models/LeaderboardEntry.cs (100%)
rename {PoFastType.Shared => src/PoFastType.Shared}/Models/UserGameResult.cs (100%)
rename {PoFastType.Shared => src/PoFastType.Shared}/Models/UserIdentity.cs (100%)
rename {PoFastType.Shared => src/PoFastType.Shared}/PoFastType.Shared.csproj (100%)
rename {PoFastType.Shared => src/PoFastType.Shared}/README.md (100%)
rename {PoFastType.Tests => tests/PoFastType.Tests}/API/Controllers/DiagControllerTests.cs (100%)
rename {PoFastType.Tests => tests/PoFastType.Tests}/API/Controllers/GameControllerTests.cs (100%)
rename {PoFastType.Tests => tests/PoFastType.Tests}/API/Controllers/ScoresControllerTests.cs (100%)
rename {PoFastType.Tests => tests/PoFastType.Tests}/E2E/HomePageE2ETests.cs (100%)
rename {PoFastType.Tests => tests/PoFastType.Tests}/E2E/LeaderboardPageE2ETests.cs (100%)
rename {PoFastType.Tests => tests/PoFastType.Tests}/E2E/ResponsiveDesignE2ETests.cs (100%)
rename {PoFastType.Tests => tests/PoFastType.Tests}/E2E/UserStatsPageE2ETests.cs (100%)
rename {PoFastType.Tests => tests/PoFastType.Tests}/Integration/AzureTableStorageIntegrationTests.cs (100%)
rename {PoFastType.Tests => tests/PoFastType.Tests}/Integration/Services/GameServiceTests.cs (100%)
rename {PoFastType.Tests => tests/PoFastType.Tests}/PoFastType.Tests.csproj (89%)
rename {PoFastType.Tests => tests/PoFastType.Tests}/README.md (100%)
rename {PoFastType.Tests => tests/PoFastType.Tests}/System/TypingGameSystemTests.cs (100%)
rename {PoFastType.Tests => tests/PoFastType.Tests}/TestHelpers/CustomWebApplicationFactory.cs (100%)
rename {PoFastType.Tests => tests/PoFastType.Tests}/Unit/Repositories/AzureTableGameResultRepositoryTests.cs (100%)
rename {PoFastType.Tests => tests/PoFastType.Tests}/Unit/Services/HardcodedTextStrategyTests.cs (100%)
rename {PoFastType.Tests => tests/PoFastType.Tests}/Unit/Services/TextGenerationServiceTests.cs (100%)
diff --git a/PoFastType.sln b/PoFastType.sln
index dee0bbb..e86da96 100644
--- a/PoFastType.sln
+++ b/PoFastType.sln
@@ -3,13 +3,13 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.0.31903.59
MinimumVisualStudioVersion = 10.0.40219.1
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PoFastType.Api", "PoFastType.Api\PoFastType.Api.csproj", "{FBD15D2B-0C27-4D6C-8BBA-17EF97728C0C}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PoFastType.Api", "src\PoFastType.Api\PoFastType.Api.csproj", "{FBD15D2B-0C27-4D6C-8BBA-17EF97728C0C}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PoFastType.Client", "PoFastType.Client\PoFastType.Client.csproj", "{42CFE93B-C8D6-4143-BB8A-60B9A5113A92}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PoFastType.Client", "src\PoFastType.Client\PoFastType.Client.csproj", "{42CFE93B-C8D6-4143-BB8A-60B9A5113A92}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PoFastType.Shared", "PoFastType.Shared\PoFastType.Shared.csproj", "{1C08E782-878D-43C1-99E5-579C9C223A51}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PoFastType.Shared", "src\PoFastType.Shared\PoFastType.Shared.csproj", "{1C08E782-878D-43C1-99E5-579C9C223A51}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PoFastType.Tests", "PoFastType.Tests\PoFastType.Tests.csproj", "{C473E3F9-0DF3-4C4A-B79E-6FA759132CD0}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PoFastType.Tests", "tests\PoFastType.Tests\PoFastType.Tests.csproj", "{C473E3F9-0DF3-4C4A-B79E-6FA759132CD0}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
diff --git a/README.md b/README.md
index e2ccd03..b6f2c36 100644
--- a/README.md
+++ b/README.md
@@ -4,7 +4,7 @@
[](https://github.com/YOUR_USERNAME/PoFastType/actions/workflows/cd.yml)
[](https://github.com/YOUR_USERNAME/PoFastType/actions/workflows/pr-validation.yml)
-A modern typing speed test application built with .NET 9 and Blazor WebAssembly. Test your typing speed, track your progress, and compete on the leaderboard!
+A modern typing speed test application built with .NET 10 and Blazor WebAssembly. Test your typing speed, track your progress, and compete on the leaderboard!
## ๐ Features
@@ -12,34 +12,36 @@ A modern typing speed test application built with .NET 9 and Blazor WebAssembly.
- **Personal Statistics** - Track your WPM, accuracy, and improvement over time
- **Global Leaderboard** - Compete with other users for the top spot
- **Responsive Design** - Works seamlessly on desktop and mobile devices
-- **Azure-Powered** - Scalable infrastructure with Azure Table Storage and Container Apps
+- **Azure-Powered** - Scalable infrastructure with Azure Table Storage and App Service
## ๐ Documentation
-- **[PRD.md](PRD.md)** - Product Requirements Document with detailed UI component specifications
+- **[docs/PRD.md](docs/PRD.md)** - Product Requirements Document with detailed UI component specifications
- **[AGENTS.md](AGENTS.md)** - AI Coding Agent Guide with project conventions and gotchas
-- **[PoFastType.Api/README.md](PoFastType.Api/README.md)** - Backend API documentation
-- **[PoFastType.Client/README.md](PoFastType.Client/README.md)** - Frontend Blazor documentation
-- **[PoFastType.Shared/README.md](PoFastType.Shared/README.md)** - Shared models and DTOs documentation
-- **[PoFastType.Tests/README.md](PoFastType.Tests/README.md)** - Comprehensive test suite documentation
+- **[docs/README.md](docs/README.md)** - Documentation index
+- **[docs/kql/](docs/kql/)** - KQL query library for Application Insights monitoring
+- **[src/PoFastType.Api/README.md](src/PoFastType.Api/README.md)** - Backend API documentation
+- **[src/PoFastType.Client/README.md](src/PoFastType.Client/README.md)** - Frontend Blazor documentation
+- **[src/PoFastType.Shared/README.md](src/PoFastType.Shared/README.md)** - Shared models and DTOs documentation
+- **[tests/PoFastType.Tests/README.md](tests/PoFastType.Tests/README.md)** - Comprehensive test suite documentation
## ๐๏ธ Architecture
This project follows **Vertical Slice Architecture** with **Clean Architecture principles**:
-- **PoFastType.Api** - ASP.NET Core Web API backend
-- **PoFastType.Client** - Blazor WebAssembly frontend (hosted in API)
-- **PoFastType.Shared** - Shared models and contracts
-- **PoFastType.Tests** - Comprehensive test suite (96 tests, 31.34% coverage)
+- **src/PoFastType.Api** - ASP.NET Core Web API backend
+- **src/PoFastType.Client** - Blazor WebAssembly frontend (hosted in API)
+- **src/PoFastType.Shared** - Shared models and contracts
+- **tests/PoFastType.Tests** - Comprehensive test suite (96 tests, 31.34% coverage)
### Technology Stack
- **Frontend:** Blazor WebAssembly, Radzen UI Components
-- **Backend:** .NET 9, ASP.NET Core Web API
-- **Database:** Azure Table Storage
-- **Monitoring:** Application Insights, Serilog
+- **Backend:** .NET 10, ASP.NET Core Web API
+- **Database:** Azure Table Storage (Azurite for local development)
+- **Monitoring:** Application Insights, Serilog, OpenTelemetry
- **CI/CD:** GitHub Actions with Azure Developer CLI
-- **Infrastructure:** Azure App Service (F1 Free Tier) + Bicep IaC
+- **Infrastructure:** Azure App Service + Bicep IaC
### Architecture Diagrams
@@ -47,50 +49,50 @@ This project follows **Vertical Slice Architecture** with **Clean Architecture p
๐ Click to view architecture diagrams
#### Project Dependencies
-
+
Simple version
-
+
#### Domain Model (Class Diagram)
-
+
Simple version
-
+
#### API Call Flow (Sequence Diagram)
-
+
Simple version
-
+
#### Game Play Use Case (Flowchart)
-
+
Simple version
-
+
#### Blazor Component Hierarchy
-
+
Simple version
-
+
## ๐ Prerequisites
-- [.NET 9.0 SDK](https://dotnet.microsoft.com/download/dotnet/9.0)
+- [.NET 10.0 SDK](https://dotnet.microsoft.com/download/dotnet/10.0)
- [Azure Developer CLI (azd)](https://aka.ms/azure-dev/install)
- [Azure CLI (az)](https://docs.microsoft.com/cli/azure/install-azure-cli)
- [Node.js](https://nodejs.org/) (for Azurite)
@@ -114,7 +116,7 @@ cd PoFastType
### 3. Run the application
```powershell
-dotnet run --project PoFastType.Api
+dotnet run --project src/PoFastType.Api
```
Or press **F5** in Visual Studio/VS Code.
@@ -123,7 +125,7 @@ Or press **F5** in Visual Studio/VS Code.
- **App:** https://localhost:5001
- **Swagger API:** https://localhost:5001/swagger
-- **Health Check:** https://localhost:5001/api/diag/health
+- **Health Check:** https://localhost:5001/api/health
- **Diagnostics:** https://localhost:5001/diag
## ๐งช Testing
@@ -275,37 +277,54 @@ The application includes comprehensive health checks:
```
PoFastType/
โโโ .github/
-โ โโโ workflows/ # GitHub Actions CI/CD workflows
-โ โโโ ci.yml # Continuous Integration (build, test, quality checks)
-โ โโโ cd.yml # Continuous Deployment (deploy to Azure)
-โ โโโ pr-validation.yml # Pull Request validation
-โโโ Diagrams/ # Mermaid architecture diagrams (.mmd and .svg)
-โโโ infra/ # Bicep infrastructure templates
-โ โโโ main.bicep # Main infrastructure template
-โ โโโ main.parameters.json # Infrastructure parameters
-โ โโโ resources.bicep # Azure resources (App Service, Storage)
-โโโ scripts/ # Automation scripts
-โ โโโ start-azurite.ps1 # Start local Azure Storage emulator
-โโโ PoFastType.Api/ # Backend API project
-โ โโโ Controllers/ # API controllers (Game, Scores, Diag, User)
-โ โโโ Services/ # Business logic services
-โ โโโ Repositories/ # Data access layer (Azure Table Storage)
-โ โโโ Middleware/ # Global exception handling (RFC 7807)
-โโโ PoFastType.Client/ # Blazor WebAssembly frontend
-โ โโโ Pages/ # Razor pages (Home, Leaderboard, UserStats, Diag)
-โ โโโ Components/ # Reusable UI components (Navbar, ErrorBoundary)
-โ โโโ Layout/ # Application layout (MainLayout)
-โ โโโ Services/ # Frontend services (GameState, UserService)
-โโโ PoFastType.Shared/ # Shared models and DTOs
-โ โโโ Models/ # Domain models (GameResult, UserIdentity, etc.)
-โโโ PoFastType.Tests/ # Test projects (96 tests, 31.34% coverage)
-โ โโโ Unit/ # Unit tests (services, repositories)
-โ โโโ Integration/ # Integration tests (with Azurite)
-โ โโโ API/ # API endpoint tests
-โ โโโ E2E/ # End-to-end tests (Playwright)
-โ โโโ System/ # System-level tests
-โโโ PRD.md # Product Requirements Document
-โโโ AGENTS.md # AI Coding Agent Guide
+โ โโโ workflows/ # GitHub Actions CI/CD workflows
+โโโ .vscode/
+โ โโโ launch.json # F5 debug configuration
+โ โโโ tasks.json # Build tasks
+โโโ docs/ # Documentation
+โ โโโ Diagrams/ # Mermaid architecture diagrams (.mmd and .svg)
+โ โโโ kql/ # KQL queries for Application Insights
+โ โโโ coverage/ # Code coverage reports
+โ โโโ PRD.md # Product Requirements Document
+โ โโโ README.md # Documentation index
+โโโ infra/ # Bicep infrastructure templates
+โ โโโ main.bicep # Main infrastructure template
+โ โโโ main.parameters.json # Infrastructure parameters
+โ โโโ resources.bicep # Azure resources (App Insights, App Service, Storage)
+โโโ scripts/ # Automation scripts
+โ โโโ start-azurite.ps1 # Start local Azure Storage emulator (Windows)
+โ โโโ start-azurite.sh # Start local Azure Storage emulator (Linux/macOS)
+โ โโโ run-coverage.ps1 # Run code coverage analysis (Windows)
+โ โโโ run-coverage.sh # Run code coverage analysis (Linux/macOS)
+โโโ src/ # Source code
+โ โโโ PoFastType.Api/ # Backend API project
+โ โ โโโ Controllers/ # API controllers (Game, Scores, Diag, User)
+โ โ โโโ Services/ # Business logic services
+โ โ โโโ Repositories/ # Data access layer (Azure Table Storage)
+โ โ โโโ Middleware/ # Global exception handling (RFC 7807)
+โ โ โโโ HealthChecks/ # Health check implementations
+โ โโโ PoFastType.Client/ # Blazor WebAssembly frontend
+โ โ โโโ Pages/ # Razor pages (Home, Leaderboard, UserStats, Diag)
+โ โ โโโ Components/ # Reusable UI components (Navbar, ErrorBoundary)
+โ โ โโโ Layout/ # Application layout (MainLayout)
+โ โ โโโ Services/ # Frontend services (GameState, UserService)
+โ โโโ PoFastType.Shared/ # Shared models and DTOs
+โ โโโ Models/ # Domain models (GameResult, UserIdentity, etc.)
+โโโ tests/ # Test projects
+โ โโโ PoFastType.Tests/ # Comprehensive test suite (96 tests, 31.34% coverage)
+โ โโโ Unit/ # Unit tests (services, repositories)
+โ โโโ Integration/ # Integration tests (with Azurite)
+โ โโโ API/ # API endpoint tests
+โ โโโ E2E/ # End-to-end tests (Playwright)
+โ โโโ System/ # System-level tests
+โโโ Directory.Packages.props # Centralized package management
+โโโ global.json # .NET SDK version lock (10.0.100)
+โโโ PoFastType.sln # Solution file
+โโโ PoFastType.http # API test collection
+โโโ AGENTS.md # AI Coding Agent Guide
+โโโ README.md # This file
+โโโ azure.yaml # Azure Developer CLI configuration
+```
โโโ azure.yaml # Azure Developer CLI configuration
```
diff --git a/Diagrams/SIMPLE_class-diagram.mmd b/docs/Diagrams/SIMPLE_class-diagram.mmd
similarity index 100%
rename from Diagrams/SIMPLE_class-diagram.mmd
rename to docs/Diagrams/SIMPLE_class-diagram.mmd
diff --git a/Diagrams/SIMPLE_class-diagram.svg b/docs/Diagrams/SIMPLE_class-diagram.svg
similarity index 100%
rename from Diagrams/SIMPLE_class-diagram.svg
rename to docs/Diagrams/SIMPLE_class-diagram.svg
diff --git a/Diagrams/SIMPLE_component-hierarchy.mmd b/docs/Diagrams/SIMPLE_component-hierarchy.mmd
similarity index 100%
rename from Diagrams/SIMPLE_component-hierarchy.mmd
rename to docs/Diagrams/SIMPLE_component-hierarchy.mmd
diff --git a/Diagrams/SIMPLE_component-hierarchy.svg b/docs/Diagrams/SIMPLE_component-hierarchy.svg
similarity index 100%
rename from Diagrams/SIMPLE_component-hierarchy.svg
rename to docs/Diagrams/SIMPLE_component-hierarchy.svg
diff --git a/Diagrams/SIMPLE_flowchart.mmd b/docs/Diagrams/SIMPLE_flowchart.mmd
similarity index 100%
rename from Diagrams/SIMPLE_flowchart.mmd
rename to docs/Diagrams/SIMPLE_flowchart.mmd
diff --git a/Diagrams/SIMPLE_flowchart.svg b/docs/Diagrams/SIMPLE_flowchart.svg
similarity index 100%
rename from Diagrams/SIMPLE_flowchart.svg
rename to docs/Diagrams/SIMPLE_flowchart.svg
diff --git a/Diagrams/SIMPLE_project-dependency.mmd b/docs/Diagrams/SIMPLE_project-dependency.mmd
similarity index 100%
rename from Diagrams/SIMPLE_project-dependency.mmd
rename to docs/Diagrams/SIMPLE_project-dependency.mmd
diff --git a/Diagrams/SIMPLE_project-dependency.svg b/docs/Diagrams/SIMPLE_project-dependency.svg
similarity index 100%
rename from Diagrams/SIMPLE_project-dependency.svg
rename to docs/Diagrams/SIMPLE_project-dependency.svg
diff --git a/Diagrams/SIMPLE_sequence-diagram.mmd b/docs/Diagrams/SIMPLE_sequence-diagram.mmd
similarity index 100%
rename from Diagrams/SIMPLE_sequence-diagram.mmd
rename to docs/Diagrams/SIMPLE_sequence-diagram.mmd
diff --git a/Diagrams/SIMPLE_sequence-diagram.svg b/docs/Diagrams/SIMPLE_sequence-diagram.svg
similarity index 100%
rename from Diagrams/SIMPLE_sequence-diagram.svg
rename to docs/Diagrams/SIMPLE_sequence-diagram.svg
diff --git a/Diagrams/class-diagram.mmd b/docs/Diagrams/class-diagram.mmd
similarity index 100%
rename from Diagrams/class-diagram.mmd
rename to docs/Diagrams/class-diagram.mmd
diff --git a/Diagrams/class-diagram.svg b/docs/Diagrams/class-diagram.svg
similarity index 100%
rename from Diagrams/class-diagram.svg
rename to docs/Diagrams/class-diagram.svg
diff --git a/Diagrams/component-hierarchy.mmd b/docs/Diagrams/component-hierarchy.mmd
similarity index 100%
rename from Diagrams/component-hierarchy.mmd
rename to docs/Diagrams/component-hierarchy.mmd
diff --git a/Diagrams/component-hierarchy.svg b/docs/Diagrams/component-hierarchy.svg
similarity index 100%
rename from Diagrams/component-hierarchy.svg
rename to docs/Diagrams/component-hierarchy.svg
diff --git a/Diagrams/flowchart.mmd b/docs/Diagrams/flowchart.mmd
similarity index 100%
rename from Diagrams/flowchart.mmd
rename to docs/Diagrams/flowchart.mmd
diff --git a/Diagrams/flowchart.svg b/docs/Diagrams/flowchart.svg
similarity index 100%
rename from Diagrams/flowchart.svg
rename to docs/Diagrams/flowchart.svg
diff --git a/Diagrams/project-dependency.mmd b/docs/Diagrams/project-dependency.mmd
similarity index 100%
rename from Diagrams/project-dependency.mmd
rename to docs/Diagrams/project-dependency.mmd
diff --git a/Diagrams/project-dependency.svg b/docs/Diagrams/project-dependency.svg
similarity index 100%
rename from Diagrams/project-dependency.svg
rename to docs/Diagrams/project-dependency.svg
diff --git a/Diagrams/sequence-diagram.mmd b/docs/Diagrams/sequence-diagram.mmd
similarity index 100%
rename from Diagrams/sequence-diagram.mmd
rename to docs/Diagrams/sequence-diagram.mmd
diff --git a/Diagrams/sequence-diagram.svg b/docs/Diagrams/sequence-diagram.svg
similarity index 100%
rename from Diagrams/sequence-diagram.svg
rename to docs/Diagrams/sequence-diagram.svg
diff --git a/PRD.md b/docs/PRD.md
similarity index 100%
rename from PRD.md
rename to docs/PRD.md
diff --git a/docs/README.md b/docs/README.md
new file mode 100644
index 0000000..d645aba
--- /dev/null
+++ b/docs/README.md
@@ -0,0 +1,28 @@
+# PoFastType Documentation
+
+This folder contains all documentation for the PoFastType application.
+
+## Contents
+
+- **[PRD.md](PRD.md)** - Product Requirements Document
+- **[Diagrams/](Diagrams/)** - Mermaid architecture diagrams (.mmd and .svg)
+- **[coverage/](coverage/)** - Code coverage reports
+- **[kql/](kql/)** - KQL query library for Application Insights
+
+## Architecture Diagrams
+
+The Diagrams folder contains:
+- Project dependencies
+- Domain model (class diagram)
+- API call flow (sequence diagram)
+- Game play use case (flowchart)
+- Blazor component hierarchy
+
+## Code Coverage
+
+Code coverage reports are generated during test runs and stored in the coverage folder.
+Target: 80% minimum line coverage for all new business logic.
+
+## KQL Queries
+
+The kql folder contains essential Kusto Query Language (KQL) queries for monitoring the application in Azure Application Insights.
diff --git a/docs/kql/app-performance.kql b/docs/kql/app-performance.kql
new file mode 100644
index 0000000..50bc6d7
--- /dev/null
+++ b/docs/kql/app-performance.kql
@@ -0,0 +1,12 @@
+// Application Performance Overview
+// Shows request count, duration, and success rate over time
+requests
+| where timestamp > ago(24h)
+| summarize
+ RequestCount = count(),
+ AvgDuration = avg(duration),
+ P95Duration = percentile(duration, 95),
+ SuccessRate = countif(success == true) * 100.0 / count()
+ by bin(timestamp, 5m)
+| order by timestamp desc
+| render timechart
diff --git a/docs/kql/endpoint-performance.kql b/docs/kql/endpoint-performance.kql
new file mode 100644
index 0000000..a5c02b8
--- /dev/null
+++ b/docs/kql/endpoint-performance.kql
@@ -0,0 +1,13 @@
+// API Endpoint Performance
+// Shows performance metrics for each API endpoint
+requests
+| where timestamp > ago(24h)
+| summarize
+ Count = count(),
+ AvgDuration = avg(duration),
+ P50 = percentile(duration, 50),
+ P95 = percentile(duration, 95),
+ P99 = percentile(duration, 99),
+ FailureRate = countif(success == false) * 100.0 / count()
+ by name
+| order by Count desc
diff --git a/docs/kql/error-analysis.kql b/docs/kql/error-analysis.kql
new file mode 100644
index 0000000..3f42689
--- /dev/null
+++ b/docs/kql/error-analysis.kql
@@ -0,0 +1,10 @@
+// Error Analysis
+// Shows exceptions and failed requests
+union exceptions, requests
+| where timestamp > ago(24h)
+| where success == false or itemType == "exception"
+| summarize
+ ErrorCount = count(),
+ Sample = any(message)
+ by problemId, type
+| order by ErrorCount desc
diff --git a/docs/kql/storage-health.kql b/docs/kql/storage-health.kql
new file mode 100644
index 0000000..3e98fbb
--- /dev/null
+++ b/docs/kql/storage-health.kql
@@ -0,0 +1,12 @@
+// Azure Storage Health
+// Monitors dependency calls to Azure Storage
+dependencies
+| where timestamp > ago(1h)
+| where type == "Azure table"
+| summarize
+ CallCount = count(),
+ AvgDuration = avg(duration),
+ FailureCount = countif(success == false),
+ SuccessRate = countif(success == true) * 100.0 / count()
+ by target, name
+| order by FailureCount desc
diff --git a/docs/kql/top-scores.kql b/docs/kql/top-scores.kql
new file mode 100644
index 0000000..7438136
--- /dev/null
+++ b/docs/kql/top-scores.kql
@@ -0,0 +1,14 @@
+// Top Game Scores
+// Shows top typing speeds and accuracy
+customMetrics
+| where timestamp > ago(7d)
+| where name in ("WordsPerMinute", "Accuracy")
+| extend userId = tostring(customDimensions.UserId)
+| summarize
+ AvgWPM = avgif(value, name == "WordsPerMinute"),
+ MaxWPM = maxif(value, name == "WordsPerMinute"),
+ AvgAccuracy = avgif(value, name == "Accuracy")
+ by userId
+| where AvgWPM > 0
+| order by MaxWPM desc
+| take 50
diff --git a/docs/kql/user-activity.kql b/docs/kql/user-activity.kql
new file mode 100644
index 0000000..5d785c5
--- /dev/null
+++ b/docs/kql/user-activity.kql
@@ -0,0 +1,12 @@
+// User Activity Tracking
+// Shows unique users and their game sessions
+customEvents
+| where timestamp > ago(7d)
+| where name in ("GameStarted", "GameCompleted")
+| extend userId = tostring(customDimensions.UserId)
+| summarize
+ SessionCount = dcount(operation_Id),
+ EventCount = count()
+ by userId, name
+| order by SessionCount desc
+| take 100
diff --git a/scripts/run-coverage.ps1 b/scripts/run-coverage.ps1
new file mode 100644
index 0000000..ae942a1
--- /dev/null
+++ b/scripts/run-coverage.ps1
@@ -0,0 +1,33 @@
+# Run code coverage analysis
+
+Write-Host "Running code coverage analysis..." -ForegroundColor Green
+
+# Clean previous coverage data
+if (Test-Path "docs/coverage") {
+ Remove-Item -Recurse -Force "docs/coverage/*"
+}
+
+# Run tests with coverage collection
+dotnet test --collect:"XPlat Code Coverage" --results-directory ./docs/coverage/raw
+
+# Generate HTML report (requires reportgenerator tool)
+$reportGeneratorInstalled = Get-Command reportgenerator -ErrorAction SilentlyContinue
+
+if ($reportGeneratorInstalled) {
+ reportgenerator `
+ -reports:"docs/coverage/raw/**/coverage.cobertura.xml" `
+ -targetdir:"docs/coverage" `
+ -reporttypes:"Html;TextSummary"
+
+ Write-Host ""
+ Write-Host "Coverage report generated at: docs/coverage/index.html" -ForegroundColor Green
+ Get-Content "docs/coverage/Summary.txt"
+}
+else {
+ Write-Host ""
+ Write-Host "Install reportgenerator for HTML reports:" -ForegroundColor Yellow
+ Write-Host "dotnet tool install -g dotnet-reportgenerator-globaltool" -ForegroundColor White
+}
+
+Write-Host ""
+Write-Host "Coverage analysis complete!" -ForegroundColor Green
diff --git a/scripts/run-coverage.sh b/scripts/run-coverage.sh
new file mode 100755
index 0000000..50313cf
--- /dev/null
+++ b/scripts/run-coverage.sh
@@ -0,0 +1,30 @@
+#!/bin/bash
+# Run code coverage analysis
+
+echo "Running code coverage analysis..."
+
+# Clean previous coverage data
+rm -rf docs/coverage/*
+
+# Run tests with coverage collection
+dotnet test --collect:"XPlat Code Coverage" --results-directory ./docs/coverage/raw
+
+# Generate HTML report (requires reportgenerator tool)
+if command -v reportgenerator &> /dev/null
+then
+ reportgenerator \
+ -reports:"docs/coverage/raw/**/coverage.cobertura.xml" \
+ -targetdir:"docs/coverage" \
+ -reporttypes:"Html;TextSummary"
+
+ echo ""
+ echo "Coverage report generated at: docs/coverage/index.html"
+ cat docs/coverage/Summary.txt
+else
+ echo ""
+ echo "Install reportgenerator for HTML reports:"
+ echo "dotnet tool install -g dotnet-reportgenerator-globaltool"
+fi
+
+echo ""
+echo "Coverage analysis complete!"
diff --git a/scripts/start-azurite.ps1 b/scripts/start-azurite.ps1
new file mode 100644
index 0000000..ad0ee00
--- /dev/null
+++ b/scripts/start-azurite.ps1
@@ -0,0 +1,23 @@
+# Start Azurite for local Azure Storage emulation
+
+Write-Host "Starting Azurite (Azure Storage Emulator)..." -ForegroundColor Green
+
+# Check if Azurite is installed
+$azuriteInstalled = Get-Command azurite -ErrorAction SilentlyContinue
+
+if (-not $azuriteInstalled) {
+ Write-Host "Azurite is not installed. Installing via npm..." -ForegroundColor Yellow
+ npm install -g azurite
+}
+
+# Start Azurite with default settings
+# - Blob storage on port 10000
+# - Queue storage on port 10001
+# - Table storage on port 10002
+Write-Host "Starting Azurite..." -ForegroundColor Cyan
+Start-Process -FilePath "azurite" -ArgumentList "--silent", "--location", "azurite-data", "--debug", "azurite-debug.log"
+
+Write-Host "Azurite started successfully!" -ForegroundColor Green
+Write-Host "Blob endpoint: http://127.0.0.1:10000" -ForegroundColor White
+Write-Host "Queue endpoint: http://127.0.0.1:10001" -ForegroundColor White
+Write-Host "Table endpoint: http://127.0.0.1:10002" -ForegroundColor White
diff --git a/scripts/start-azurite.sh b/scripts/start-azurite.sh
new file mode 100755
index 0000000..af211b0
--- /dev/null
+++ b/scripts/start-azurite.sh
@@ -0,0 +1,22 @@
+#!/bin/bash
+# Start Azurite for local Azure Storage emulation
+
+echo "Starting Azurite (Azure Storage Emulator)..."
+
+# Check if Azurite is installed
+if ! command -v azurite &> /dev/null
+then
+ echo "Azurite is not installed. Installing via npm..."
+ npm install -g azurite
+fi
+
+# Start Azurite with default settings
+# - Blob storage on port 10000
+# - Queue storage on port 10001
+# - Table storage on port 10002
+azurite --silent --location azurite-data --debug azurite-debug.log
+
+echo "Azurite started successfully!"
+echo "Blob endpoint: http://127.0.0.1:10000"
+echo "Queue endpoint: http://127.0.0.1:10001"
+echo "Table endpoint: http://127.0.0.1:10002"
diff --git a/PoFastType.Api/Controllers/DiagController.cs b/src/PoFastType.Api/Controllers/DiagController.cs
similarity index 100%
rename from PoFastType.Api/Controllers/DiagController.cs
rename to src/PoFastType.Api/Controllers/DiagController.cs
diff --git a/PoFastType.Api/Controllers/GameController.cs b/src/PoFastType.Api/Controllers/GameController.cs
similarity index 100%
rename from PoFastType.Api/Controllers/GameController.cs
rename to src/PoFastType.Api/Controllers/GameController.cs
diff --git a/PoFastType.Api/Controllers/ScoresController.cs b/src/PoFastType.Api/Controllers/ScoresController.cs
similarity index 100%
rename from PoFastType.Api/Controllers/ScoresController.cs
rename to src/PoFastType.Api/Controllers/ScoresController.cs
diff --git a/PoFastType.Api/Controllers/UserController.cs b/src/PoFastType.Api/Controllers/UserController.cs
similarity index 100%
rename from PoFastType.Api/Controllers/UserController.cs
rename to src/PoFastType.Api/Controllers/UserController.cs
diff --git a/PoFastType.Api/HealthChecks/AzureTableStorageHealthCheck.cs b/src/PoFastType.Api/HealthChecks/AzureTableStorageHealthCheck.cs
similarity index 100%
rename from PoFastType.Api/HealthChecks/AzureTableStorageHealthCheck.cs
rename to src/PoFastType.Api/HealthChecks/AzureTableStorageHealthCheck.cs
diff --git a/PoFastType.Api/Middleware/GlobalExceptionMiddleware.cs b/src/PoFastType.Api/Middleware/GlobalExceptionMiddleware.cs
similarity index 100%
rename from PoFastType.Api/Middleware/GlobalExceptionMiddleware.cs
rename to src/PoFastType.Api/Middleware/GlobalExceptionMiddleware.cs
diff --git a/PoFastType.Api/PoFastType.Api.csproj b/src/PoFastType.Api/PoFastType.Api.csproj
similarity index 100%
rename from PoFastType.Api/PoFastType.Api.csproj
rename to src/PoFastType.Api/PoFastType.Api.csproj
diff --git a/PoFastType.Api/Program.cs b/src/PoFastType.Api/Program.cs
similarity index 100%
rename from PoFastType.Api/Program.cs
rename to src/PoFastType.Api/Program.cs
diff --git a/PoFastType.Api/Properties/ServiceDependencies/PoFastType - Web Deploy/profile.arm.json b/src/PoFastType.Api/Properties/ServiceDependencies/PoFastType - Web Deploy/profile.arm.json
similarity index 100%
rename from PoFastType.Api/Properties/ServiceDependencies/PoFastType - Web Deploy/profile.arm.json
rename to src/PoFastType.Api/Properties/ServiceDependencies/PoFastType - Web Deploy/profile.arm.json
diff --git a/PoFastType.Api/Properties/launchSettings.json b/src/PoFastType.Api/Properties/launchSettings.json
similarity index 100%
rename from PoFastType.Api/Properties/launchSettings.json
rename to src/PoFastType.Api/Properties/launchSettings.json
diff --git a/PoFastType.Api/README.md b/src/PoFastType.Api/README.md
similarity index 100%
rename from PoFastType.Api/README.md
rename to src/PoFastType.Api/README.md
diff --git a/PoFastType.Api/Repositories/AzureTableGameResultRepository.cs b/src/PoFastType.Api/Repositories/AzureTableGameResultRepository.cs
similarity index 100%
rename from PoFastType.Api/Repositories/AzureTableGameResultRepository.cs
rename to src/PoFastType.Api/Repositories/AzureTableGameResultRepository.cs
diff --git a/PoFastType.Api/Repositories/IGameResultRepository.cs b/src/PoFastType.Api/Repositories/IGameResultRepository.cs
similarity index 100%
rename from PoFastType.Api/Repositories/IGameResultRepository.cs
rename to src/PoFastType.Api/Repositories/IGameResultRepository.cs
diff --git a/PoFastType.Api/Services/GameService.cs b/src/PoFastType.Api/Services/GameService.cs
similarity index 100%
rename from PoFastType.Api/Services/GameService.cs
rename to src/PoFastType.Api/Services/GameService.cs
diff --git a/PoFastType.Api/Services/HardcodedTextStrategy.cs b/src/PoFastType.Api/Services/HardcodedTextStrategy.cs
similarity index 100%
rename from PoFastType.Api/Services/HardcodedTextStrategy.cs
rename to src/PoFastType.Api/Services/HardcodedTextStrategy.cs
diff --git a/PoFastType.Api/Services/IGameService.cs b/src/PoFastType.Api/Services/IGameService.cs
similarity index 100%
rename from PoFastType.Api/Services/IGameService.cs
rename to src/PoFastType.Api/Services/IGameService.cs
diff --git a/PoFastType.Api/Services/ITextGenerationService.cs b/src/PoFastType.Api/Services/ITextGenerationService.cs
similarity index 100%
rename from PoFastType.Api/Services/ITextGenerationService.cs
rename to src/PoFastType.Api/Services/ITextGenerationService.cs
diff --git a/PoFastType.Api/Services/ITextGenerationStrategy.cs b/src/PoFastType.Api/Services/ITextGenerationStrategy.cs
similarity index 100%
rename from PoFastType.Api/Services/ITextGenerationStrategy.cs
rename to src/PoFastType.Api/Services/ITextGenerationStrategy.cs
diff --git a/PoFastType.Api/Services/IUserIdentityService.cs b/src/PoFastType.Api/Services/IUserIdentityService.cs
similarity index 100%
rename from PoFastType.Api/Services/IUserIdentityService.cs
rename to src/PoFastType.Api/Services/IUserIdentityService.cs
diff --git a/PoFastType.Api/Services/TextGenerationService.cs b/src/PoFastType.Api/Services/TextGenerationService.cs
similarity index 100%
rename from PoFastType.Api/Services/TextGenerationService.cs
rename to src/PoFastType.Api/Services/TextGenerationService.cs
diff --git a/PoFastType.Api/Services/UserIdentityService.cs b/src/PoFastType.Api/Services/UserIdentityService.cs
similarity index 100%
rename from PoFastType.Api/Services/UserIdentityService.cs
rename to src/PoFastType.Api/Services/UserIdentityService.cs
diff --git a/PoFastType.Api/appsettings.Development.json b/src/PoFastType.Api/appsettings.Development.json
similarity index 100%
rename from PoFastType.Api/appsettings.Development.json
rename to src/PoFastType.Api/appsettings.Development.json
diff --git a/PoFastType.Api/appsettings.Production.json b/src/PoFastType.Api/appsettings.Production.json
similarity index 100%
rename from PoFastType.Api/appsettings.Production.json
rename to src/PoFastType.Api/appsettings.Production.json
diff --git a/PoFastType.Api/appsettings.json b/src/PoFastType.Api/appsettings.json
similarity index 100%
rename from PoFastType.Api/appsettings.json
rename to src/PoFastType.Api/appsettings.json
diff --git a/PoFastType.Api/log.txt b/src/PoFastType.Api/log.txt
similarity index 100%
rename from PoFastType.Api/log.txt
rename to src/PoFastType.Api/log.txt
diff --git a/PoFastType.Api/web.config b/src/PoFastType.Api/web.config
similarity index 100%
rename from PoFastType.Api/web.config
rename to src/PoFastType.Api/web.config
diff --git a/PoFastType.Client/App.razor b/src/PoFastType.Client/App.razor
similarity index 100%
rename from PoFastType.Client/App.razor
rename to src/PoFastType.Client/App.razor
diff --git a/PoFastType.Client/Components/ErrorBoundary.razor b/src/PoFastType.Client/Components/ErrorBoundary.razor
similarity index 100%
rename from PoFastType.Client/Components/ErrorBoundary.razor
rename to src/PoFastType.Client/Components/ErrorBoundary.razor
diff --git a/PoFastType.Client/Components/Navbar_New.razor b/src/PoFastType.Client/Components/Navbar_New.razor
similarity index 100%
rename from PoFastType.Client/Components/Navbar_New.razor
rename to src/PoFastType.Client/Components/Navbar_New.razor
diff --git a/PoFastType.Client/Layout/MainLayout.razor b/src/PoFastType.Client/Layout/MainLayout.razor
similarity index 100%
rename from PoFastType.Client/Layout/MainLayout.razor
rename to src/PoFastType.Client/Layout/MainLayout.razor
diff --git a/PoFastType.Client/Pages/Diag.razor b/src/PoFastType.Client/Pages/Diag.razor
similarity index 100%
rename from PoFastType.Client/Pages/Diag.razor
rename to src/PoFastType.Client/Pages/Diag.razor
diff --git a/PoFastType.Client/Pages/Home.razor b/src/PoFastType.Client/Pages/Home.razor
similarity index 100%
rename from PoFastType.Client/Pages/Home.razor
rename to src/PoFastType.Client/Pages/Home.razor
diff --git a/PoFastType.Client/Pages/Leaderboard.razor b/src/PoFastType.Client/Pages/Leaderboard.razor
similarity index 100%
rename from PoFastType.Client/Pages/Leaderboard.razor
rename to src/PoFastType.Client/Pages/Leaderboard.razor
diff --git a/PoFastType.Client/Pages/UserStats.razor b/src/PoFastType.Client/Pages/UserStats.razor
similarity index 100%
rename from PoFastType.Client/Pages/UserStats.razor
rename to src/PoFastType.Client/Pages/UserStats.razor
diff --git a/PoFastType.Client/PoFastType.Client.csproj b/src/PoFastType.Client/PoFastType.Client.csproj
similarity index 100%
rename from PoFastType.Client/PoFastType.Client.csproj
rename to src/PoFastType.Client/PoFastType.Client.csproj
diff --git a/PoFastType.Client/Program.cs b/src/PoFastType.Client/Program.cs
similarity index 100%
rename from PoFastType.Client/Program.cs
rename to src/PoFastType.Client/Program.cs
diff --git a/PoFastType.Client/Properties/launchSettings.json b/src/PoFastType.Client/Properties/launchSettings.json
similarity index 100%
rename from PoFastType.Client/Properties/launchSettings.json
rename to src/PoFastType.Client/Properties/launchSettings.json
diff --git a/PoFastType.Client/README.md b/src/PoFastType.Client/README.md
similarity index 100%
rename from PoFastType.Client/README.md
rename to src/PoFastType.Client/README.md
diff --git a/PoFastType.Client/Services/GameStateService.cs b/src/PoFastType.Client/Services/GameStateService.cs
similarity index 100%
rename from PoFastType.Client/Services/GameStateService.cs
rename to src/PoFastType.Client/Services/GameStateService.cs
diff --git a/PoFastType.Client/Services/IUserService.cs b/src/PoFastType.Client/Services/IUserService.cs
similarity index 100%
rename from PoFastType.Client/Services/IUserService.cs
rename to src/PoFastType.Client/Services/IUserService.cs
diff --git a/PoFastType.Client/Services/UserService.cs b/src/PoFastType.Client/Services/UserService.cs
similarity index 100%
rename from PoFastType.Client/Services/UserService.cs
rename to src/PoFastType.Client/Services/UserService.cs
diff --git a/PoFastType.Client/_Imports.razor b/src/PoFastType.Client/_Imports.razor
similarity index 100%
rename from PoFastType.Client/_Imports.razor
rename to src/PoFastType.Client/_Imports.razor
diff --git a/PoFastType.Client/wwwroot/app.js b/src/PoFastType.Client/wwwroot/app.js
similarity index 100%
rename from PoFastType.Client/wwwroot/app.js
rename to src/PoFastType.Client/wwwroot/app.js
diff --git a/PoFastType.Client/wwwroot/appsettings.json b/src/PoFastType.Client/wwwroot/appsettings.json
similarity index 100%
rename from PoFastType.Client/wwwroot/appsettings.json
rename to src/PoFastType.Client/wwwroot/appsettings.json
diff --git a/PoFastType.Client/wwwroot/css/retro-arcade.css b/src/PoFastType.Client/wwwroot/css/retro-arcade.css
similarity index 100%
rename from PoFastType.Client/wwwroot/css/retro-arcade.css
rename to src/PoFastType.Client/wwwroot/css/retro-arcade.css
diff --git a/PoFastType.Client/wwwroot/favicon.png b/src/PoFastType.Client/wwwroot/favicon.png
similarity index 100%
rename from PoFastType.Client/wwwroot/favicon.png
rename to src/PoFastType.Client/wwwroot/favicon.png
diff --git a/PoFastType.Client/wwwroot/icon-192.png b/src/PoFastType.Client/wwwroot/icon-192.png
similarity index 100%
rename from PoFastType.Client/wwwroot/icon-192.png
rename to src/PoFastType.Client/wwwroot/icon-192.png
diff --git a/PoFastType.Client/wwwroot/index.html b/src/PoFastType.Client/wwwroot/index.html
similarity index 100%
rename from PoFastType.Client/wwwroot/index.html
rename to src/PoFastType.Client/wwwroot/index.html
diff --git a/PoFastType.Client/wwwroot/sample-data/weather.json b/src/PoFastType.Client/wwwroot/sample-data/weather.json
similarity index 100%
rename from PoFastType.Client/wwwroot/sample-data/weather.json
rename to src/PoFastType.Client/wwwroot/sample-data/weather.json
diff --git a/PoFastType.Shared/Models/GameResult.cs b/src/PoFastType.Shared/Models/GameResult.cs
similarity index 100%
rename from PoFastType.Shared/Models/GameResult.cs
rename to src/PoFastType.Shared/Models/GameResult.cs
diff --git a/PoFastType.Shared/Models/LeaderboardEntry.cs b/src/PoFastType.Shared/Models/LeaderboardEntry.cs
similarity index 100%
rename from PoFastType.Shared/Models/LeaderboardEntry.cs
rename to src/PoFastType.Shared/Models/LeaderboardEntry.cs
diff --git a/PoFastType.Shared/Models/UserGameResult.cs b/src/PoFastType.Shared/Models/UserGameResult.cs
similarity index 100%
rename from PoFastType.Shared/Models/UserGameResult.cs
rename to src/PoFastType.Shared/Models/UserGameResult.cs
diff --git a/PoFastType.Shared/Models/UserIdentity.cs b/src/PoFastType.Shared/Models/UserIdentity.cs
similarity index 100%
rename from PoFastType.Shared/Models/UserIdentity.cs
rename to src/PoFastType.Shared/Models/UserIdentity.cs
diff --git a/PoFastType.Shared/PoFastType.Shared.csproj b/src/PoFastType.Shared/PoFastType.Shared.csproj
similarity index 100%
rename from PoFastType.Shared/PoFastType.Shared.csproj
rename to src/PoFastType.Shared/PoFastType.Shared.csproj
diff --git a/PoFastType.Shared/README.md b/src/PoFastType.Shared/README.md
similarity index 100%
rename from PoFastType.Shared/README.md
rename to src/PoFastType.Shared/README.md
diff --git a/PoFastType.Tests/API/Controllers/DiagControllerTests.cs b/tests/PoFastType.Tests/API/Controllers/DiagControllerTests.cs
similarity index 100%
rename from PoFastType.Tests/API/Controllers/DiagControllerTests.cs
rename to tests/PoFastType.Tests/API/Controllers/DiagControllerTests.cs
diff --git a/PoFastType.Tests/API/Controllers/GameControllerTests.cs b/tests/PoFastType.Tests/API/Controllers/GameControllerTests.cs
similarity index 100%
rename from PoFastType.Tests/API/Controllers/GameControllerTests.cs
rename to tests/PoFastType.Tests/API/Controllers/GameControllerTests.cs
diff --git a/PoFastType.Tests/API/Controllers/ScoresControllerTests.cs b/tests/PoFastType.Tests/API/Controllers/ScoresControllerTests.cs
similarity index 100%
rename from PoFastType.Tests/API/Controllers/ScoresControllerTests.cs
rename to tests/PoFastType.Tests/API/Controllers/ScoresControllerTests.cs
diff --git a/PoFastType.Tests/E2E/HomePageE2ETests.cs b/tests/PoFastType.Tests/E2E/HomePageE2ETests.cs
similarity index 100%
rename from PoFastType.Tests/E2E/HomePageE2ETests.cs
rename to tests/PoFastType.Tests/E2E/HomePageE2ETests.cs
diff --git a/PoFastType.Tests/E2E/LeaderboardPageE2ETests.cs b/tests/PoFastType.Tests/E2E/LeaderboardPageE2ETests.cs
similarity index 100%
rename from PoFastType.Tests/E2E/LeaderboardPageE2ETests.cs
rename to tests/PoFastType.Tests/E2E/LeaderboardPageE2ETests.cs
diff --git a/PoFastType.Tests/E2E/ResponsiveDesignE2ETests.cs b/tests/PoFastType.Tests/E2E/ResponsiveDesignE2ETests.cs
similarity index 100%
rename from PoFastType.Tests/E2E/ResponsiveDesignE2ETests.cs
rename to tests/PoFastType.Tests/E2E/ResponsiveDesignE2ETests.cs
diff --git a/PoFastType.Tests/E2E/UserStatsPageE2ETests.cs b/tests/PoFastType.Tests/E2E/UserStatsPageE2ETests.cs
similarity index 100%
rename from PoFastType.Tests/E2E/UserStatsPageE2ETests.cs
rename to tests/PoFastType.Tests/E2E/UserStatsPageE2ETests.cs
diff --git a/PoFastType.Tests/Integration/AzureTableStorageIntegrationTests.cs b/tests/PoFastType.Tests/Integration/AzureTableStorageIntegrationTests.cs
similarity index 100%
rename from PoFastType.Tests/Integration/AzureTableStorageIntegrationTests.cs
rename to tests/PoFastType.Tests/Integration/AzureTableStorageIntegrationTests.cs
diff --git a/PoFastType.Tests/Integration/Services/GameServiceTests.cs b/tests/PoFastType.Tests/Integration/Services/GameServiceTests.cs
similarity index 100%
rename from PoFastType.Tests/Integration/Services/GameServiceTests.cs
rename to tests/PoFastType.Tests/Integration/Services/GameServiceTests.cs
diff --git a/PoFastType.Tests/PoFastType.Tests.csproj b/tests/PoFastType.Tests/PoFastType.Tests.csproj
similarity index 89%
rename from PoFastType.Tests/PoFastType.Tests.csproj
rename to tests/PoFastType.Tests/PoFastType.Tests.csproj
index b069fae..83c556a 100644
--- a/PoFastType.Tests/PoFastType.Tests.csproj
+++ b/tests/PoFastType.Tests/PoFastType.Tests.csproj
@@ -35,8 +35,8 @@
-
-
+
+
diff --git a/PoFastType.Tests/README.md b/tests/PoFastType.Tests/README.md
similarity index 100%
rename from PoFastType.Tests/README.md
rename to tests/PoFastType.Tests/README.md
diff --git a/PoFastType.Tests/System/TypingGameSystemTests.cs b/tests/PoFastType.Tests/System/TypingGameSystemTests.cs
similarity index 100%
rename from PoFastType.Tests/System/TypingGameSystemTests.cs
rename to tests/PoFastType.Tests/System/TypingGameSystemTests.cs
diff --git a/PoFastType.Tests/TestHelpers/CustomWebApplicationFactory.cs b/tests/PoFastType.Tests/TestHelpers/CustomWebApplicationFactory.cs
similarity index 100%
rename from PoFastType.Tests/TestHelpers/CustomWebApplicationFactory.cs
rename to tests/PoFastType.Tests/TestHelpers/CustomWebApplicationFactory.cs
diff --git a/PoFastType.Tests/Unit/Repositories/AzureTableGameResultRepositoryTests.cs b/tests/PoFastType.Tests/Unit/Repositories/AzureTableGameResultRepositoryTests.cs
similarity index 100%
rename from PoFastType.Tests/Unit/Repositories/AzureTableGameResultRepositoryTests.cs
rename to tests/PoFastType.Tests/Unit/Repositories/AzureTableGameResultRepositoryTests.cs
diff --git a/PoFastType.Tests/Unit/Services/HardcodedTextStrategyTests.cs b/tests/PoFastType.Tests/Unit/Services/HardcodedTextStrategyTests.cs
similarity index 100%
rename from PoFastType.Tests/Unit/Services/HardcodedTextStrategyTests.cs
rename to tests/PoFastType.Tests/Unit/Services/HardcodedTextStrategyTests.cs
diff --git a/PoFastType.Tests/Unit/Services/TextGenerationServiceTests.cs b/tests/PoFastType.Tests/Unit/Services/TextGenerationServiceTests.cs
similarity index 100%
rename from PoFastType.Tests/Unit/Services/TextGenerationServiceTests.cs
rename to tests/PoFastType.Tests/Unit/Services/TextGenerationServiceTests.cs
From a59be6655a22950e918535f8258aaa4f1de17600 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sun, 23 Nov 2025 16:27:36 +0000
Subject: [PATCH 4/6] Add Serilog config, Azure Key Vault, and OpenTelemetry
support
Co-authored-by: punkouter26 <121304072+punkouter26@users.noreply.github.com>
---
Directory.Packages.props | 3 +
src/PoFastType.Api/PoFastType.Api.csproj | 3 +
src/PoFastType.Api/Program.cs | 73 ++++++++++++-------
.../appsettings.Development.json | 25 +++----
.../appsettings.Production.json | 22 +++++-
src/PoFastType.Api/appsettings.json | 44 +++++++++--
6 files changed, 122 insertions(+), 48 deletions(-)
diff --git a/Directory.Packages.props b/Directory.Packages.props
index cbe75a9..7adc001 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -9,12 +9,15 @@
+
+
+
diff --git a/src/PoFastType.Api/PoFastType.Api.csproj b/src/PoFastType.Api/PoFastType.Api.csproj
index dcd2524..f1805ee 100644
--- a/src/PoFastType.Api/PoFastType.Api.csproj
+++ b/src/PoFastType.Api/PoFastType.Api.csproj
@@ -11,6 +11,7 @@
+
@@ -23,6 +24,8 @@
+
+
diff --git a/src/PoFastType.Api/Program.cs b/src/PoFastType.Api/Program.cs
index 0734f74..e9a1c03 100644
--- a/src/PoFastType.Api/Program.cs
+++ b/src/PoFastType.Api/Program.cs
@@ -6,32 +6,38 @@
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Diagnostics.HealthChecks;
using Serilog;
-using Serilog.Formatting.Compact;
-using System.IO;
+using Azure.Identity;
+using Azure.Security.KeyVault.Secrets;
+using OpenTelemetry.Metrics;
+using OpenTelemetry.Trace;
+using OpenTelemetry.Resources;
var builder = WebApplication.CreateBuilder(args);
-// Ensure DEBUG directory exists
-var debugPath = Path.Combine(Directory.GetCurrentDirectory(), "..", "DEBUG");
-Directory.CreateDirectory(debugPath);
+// Configure Azure Key Vault for Production only
+if (builder.Environment.IsProduction())
+{
+ var keyVaultUri = builder.Configuration["AzureKeyVault:VaultUri"];
+ if (!string.IsNullOrEmpty(keyVaultUri))
+ {
+ var secretClient = new SecretClient(
+ new Uri(keyVaultUri),
+ new DefaultAzureCredential());
+
+ builder.Configuration.AddAzureKeyVault(
+ new Uri(keyVaultUri),
+ new DefaultAzureCredential());
+ }
+}
-// Configure Serilog with structured JSON logging and overwrite behavior
+// Configure Serilog from appsettings.json
Log.Logger = new LoggerConfiguration()
- .MinimumLevel.Debug()
- .WriteTo.Console(new CompactJsonFormatter())
- .WriteTo.File(
- new CompactJsonFormatter(),
- Path.Combine(debugPath, "log.txt"),
- rollingInterval: RollingInterval.Infinite,
- rollOnFileSizeLimit: false,
- shared: false,
- buffered: false,
- flushToDiskInterval: TimeSpan.FromSeconds(1))
+ .ReadFrom.Configuration(builder.Configuration)
+ .Enrich.FromLogContext()
.Enrich.WithProperty("Application", "PoFastType")
.Enrich.WithProperty("Environment", builder.Environment.EnvironmentName)
.CreateLogger();
-// Log application startup
Log.Information("PoFastType application starting up at {Timestamp}", DateTime.UtcNow);
builder.Host.UseSerilog();
@@ -39,13 +45,27 @@
// Add Application Insights
builder.Services.AddApplicationInsightsTelemetry();
-// Configure logging to write to console and file
+// Add OpenTelemetry
+builder.Services.AddOpenTelemetry()
+ .ConfigureResource(resource => resource
+ .AddService("PoFastType")
+ .AddAttributes(new Dictionary
+ {
+ ["deployment.environment"] = builder.Environment.EnvironmentName
+ }))
+ .WithMetrics(metrics => metrics
+ .AddAspNetCoreInstrumentation()
+ .AddRuntimeInstrumentation()
+ .AddMeter("PoFastType.Metrics"))
+ .WithTracing(tracing => tracing
+ .AddAspNetCoreInstrumentation()
+ .AddHttpClientInstrumentation());
+
+// Configure logging
builder.Logging.ClearProviders();
builder.Logging.AddSerilog();
-builder.Logging.SetMinimumLevel(LogLevel.Debug);
-// Add services to the container.
-// Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi
+// Add services to the container
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
@@ -70,8 +90,10 @@
}
else
{
- // In production, be more restrictive
- policy.WithOrigins("https://pofasttype.azurewebsites.net")
+ // In production, use configured origins
+ var allowedOrigins = builder.Configuration.GetSection("CORS:AllowedOrigins").Get()
+ ?? new[] { "https://pofasttype.azurewebsites.net" };
+ policy.WithOrigins(allowedOrigins)
.AllowAnyHeader()
.AllowAnyMethod()
.AllowCredentials();
@@ -101,10 +123,10 @@
// Add global exception handling middleware first
app.UseMiddleware();
-// Configure the HTTP request pipeline.
+// Configure the HTTP request pipeline
if (app.Environment.IsDevelopment())
{
- app.UseDeveloperExceptionPage(); // Add this for detailed error pages in development
+ app.UseDeveloperExceptionPage();
app.UseSwagger();
app.UseSwaggerUI();
app.UseWebAssemblyDebugging();
@@ -150,7 +172,6 @@
// Fallback to serve the Blazor WebAssembly app for non-API routes
app.MapFallbackToFile("index.html");
-// Log application ready state
Log.Information("PoFastType application configured and ready to serve requests");
try
diff --git a/src/PoFastType.Api/appsettings.Development.json b/src/PoFastType.Api/appsettings.Development.json
index 26a4e9c..1f0a572 100644
--- a/src/PoFastType.Api/appsettings.Development.json
+++ b/src/PoFastType.Api/appsettings.Development.json
@@ -1,4 +1,15 @@
{
+ "Serilog": {
+ "MinimumLevel": {
+ "Default": "Debug",
+ "Override": {
+ "Microsoft": "Information",
+ "Microsoft.AspNetCore": "Information",
+ "PoFastType.Api.Middleware": "Debug",
+ "PoFastType.Api.Controllers": "Debug"
+ }
+ }
+ },
"Logging": {
"LogLevel": {
"Default": "Debug",
@@ -13,13 +24,6 @@
},
"Console": {
"IncludeScopes": true
- },
- "File": {
- "Path": "log.txt",
- "Append": false,
- "MinLevel": "Debug",
- "FileSizeLimitBytes": 10485760,
- "MaxRollingFiles": 1
}
},
"DebugSettings": {
@@ -37,12 +41,7 @@
"TableName": "PoFastTypeGameResults"
},
"ApplicationInsights": {
- "ConnectionString": "InstrumentationKey=your-ai-key;IngestionEndpoint=https://eastus-8.in.applicationinsights.azure.com/;LiveEndpoint=https://eastus.livediagnostics.monitor.azure.com/;ApplicationId=your-app-id"
- },
- "AzureOpenAI": {
- "Endpoint": "https://posharedopenai.openai.azure.com/",
- "ApiKey": "your-openai-key",
- "DeploymentName": "gpt-4"
+ "ConnectionString": ""
},
"AllowedHosts": "*"
}
diff --git a/src/PoFastType.Api/appsettings.Production.json b/src/PoFastType.Api/appsettings.Production.json
index a769b52..8500485 100644
--- a/src/PoFastType.Api/appsettings.Production.json
+++ b/src/PoFastType.Api/appsettings.Production.json
@@ -1,4 +1,20 @@
{
+ "Serilog": {
+ "MinimumLevel": {
+ "Default": "Information",
+ "Override": {
+ "Microsoft": "Warning",
+ "Microsoft.AspNetCore": "Warning",
+ "Microsoft.EntityFrameworkCore": "Warning",
+ "System.Net.Http.HttpClient": "Warning"
+ }
+ },
+ "WriteTo": [
+ {
+ "Name": "Console"
+ }
+ ]
+ },
"Logging": {
"LogLevel": {
"Default": "Information",
@@ -9,11 +25,11 @@
},
"AllowedHosts": "*",
"AzureTableStorage": {
- "ConnectionString": "",
"TableName": "PoFastTypeGameResults"
},
"ApplicationInsights": {
- "ConnectionString": ""
+ "EnableAdaptiveSampling": true,
+ "EnablePerformanceCounterCollectionModule": true
},
"HealthChecks": {
"TimeoutSeconds": 30,
@@ -21,7 +37,7 @@
},
"CORS": {
"AllowedOrigins": [
- "https://pofasttype.azurecontainerapps.io"
+ "https://pofasttype.azurewebsites.net"
]
}
}
diff --git a/src/PoFastType.Api/appsettings.json b/src/PoFastType.Api/appsettings.json
index f1a0a2f..b0a74a3 100644
--- a/src/PoFastType.Api/appsettings.json
+++ b/src/PoFastType.Api/appsettings.json
@@ -1,4 +1,38 @@
{
+ "Serilog": {
+ "Using": [ "Serilog.Sinks.Console", "Serilog.Sinks.File" ],
+ "MinimumLevel": {
+ "Default": "Information",
+ "Override": {
+ "Microsoft": "Warning",
+ "Microsoft.AspNetCore": "Warning",
+ "System": "Warning"
+ }
+ },
+ "WriteTo": [
+ {
+ "Name": "Console",
+ "Args": {
+ "formatter": "Serilog.Formatting.Compact.CompactJsonFormatter, Serilog.Formatting.Compact"
+ }
+ },
+ {
+ "Name": "File",
+ "Args": {
+ "path": "../DEBUG/log.txt",
+ "formatter": "Serilog.Formatting.Compact.CompactJsonFormatter, Serilog.Formatting.Compact",
+ "rollingInterval": "Infinite",
+ "rollOnFileSizeLimit": false,
+ "shared": false,
+ "flushToDiskInterval": "00:00:01"
+ }
+ }
+ ],
+ "Enrich": [ "FromLogContext", "WithMachineName", "WithThreadId" ],
+ "Properties": {
+ "Application": "PoFastType"
+ }
+ },
"Logging": {
"LogLevel": {
"Default": "Information",
@@ -16,15 +50,13 @@
"MinimumLogLevel": "Debug"
},
"AzureTableStorage": {
- "ConnectionString": "DefaultEndpointsProtocol=https;EndpointSuffix=core.windows.net;AccountName=posharedtablestorage;AccountKey=LPMW8tZgPJJnYLhCRME+KW4fViv3rJZA+XrycWCjO89yFOMaE2Qi7m3IAkb5dtdD8cR6SFk478b++ASt5ZuqfA==;BlobEndpoint=https://posharedtablestorage.blob.core.windows.net/;FileEndpoint=https://posharedtablestorage.file.core.windows.net/;QueueEndpoint=https://posharedtablestorage.queue.core.windows.net/;TableEndpoint=https://posharedtablestorage.table.core.windows.net/",
+ "ConnectionString": "UseDevelopmentStorage=true",
"TableName": "PoFastTypeGameResults"
},
"ApplicationInsights": {
- "ConnectionString": "InstrumentationKey=7027f796-a9be-4543-8ffd-bf655ae47a44;IngestionEndpoint=https://eastus-8.in.applicationinsights.azure.com/;LiveEndpoint=https://eastus.livediagnostics.monitor.azure.com/;ApplicationId=7530837a-f820-4dd4-8593-daade7bfb589"
+ "ConnectionString": ""
},
- "AzureOpenAI": {
- "Endpoint": "https://posharedopenai.openai.azure.com/",
- "ApiKey": "your-openai-key",
- "DeploymentName": "gpt-4"
+ "AzureKeyVault": {
+ "VaultUri": "https://pofasttype-kv.vault.azure.net/"
}
}
From fdaf5546e8ba88c71be778b2ae59898209289fb7 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sun, 23 Nov 2025 16:29:32 +0000
Subject: [PATCH 5/6] Update Bicep infrastructure: Key Vault, budget alerts,
Snapshot Debugger, .NET 10
Co-authored-by: punkouter26 <121304072+punkouter26@users.noreply.github.com>
---
infra/budget.bicep | 60 ++++++++++++++++++++++++++++++++++
infra/main.bicep | 15 +++++++++
infra/resources.bicep | 76 +++++++++++++++++++++++++++++++++++++++++--
3 files changed, 149 insertions(+), 2 deletions(-)
create mode 100644 infra/budget.bicep
diff --git a/infra/budget.bicep b/infra/budget.bicep
new file mode 100644
index 0000000..371e405
--- /dev/null
+++ b/infra/budget.bicep
@@ -0,0 +1,60 @@
+targetScope = 'resourceGroup'
+
+@description('Name of the budget')
+param budgetName string = 'PoFastType-MonthlyBudget'
+
+@description('The total amount of cost or usage to track with the budget')
+param amount int = 5
+
+@description('The time covered by a budget. Tracking of the amount will be reset based on the time grain.')
+@allowed([
+ 'Monthly'
+ 'Quarterly'
+ 'Annually'
+ 'BillingMonth'
+ 'BillingQuarter'
+ 'BillingAnnual'
+])
+param timeGrain string = 'Monthly'
+
+@description('The start date for the budget')
+param startDate string = utcNow('yyyy-MM-01')
+
+@description('The end date for the budget (5 years from now)')
+param endDate string = dateTimeAdd(utcNow(), 'P5Y', 'yyyy-MM-dd')
+
+@description('Action Group Resource ID for budget alerts')
+param actionGroupId string
+
+@description('Threshold percentage for alert (default: 80%)')
+param thresholdPercentage int = 80
+
+resource budget 'Microsoft.Consumption/budgets@2023-11-01' = {
+ name: budgetName
+ properties: {
+ timePeriod: {
+ startDate: startDate
+ endDate: endDate
+ }
+ timeGrain: timeGrain
+ amount: amount
+ category: 'Cost'
+ notifications: {
+ 'Actual_GreaterThan_${thresholdPercentage}_Percent': {
+ enabled: true
+ operator: 'GreaterThan'
+ threshold: thresholdPercentage
+ contactEmails: [
+ 'punkouter26@gmail.com'
+ ]
+ contactGroups: [
+ actionGroupId
+ ]
+ thresholdType: 'Actual'
+ }
+ }
+ }
+}
+
+output budgetId string = budget.id
+output budgetName string = budget.name
diff --git a/infra/main.bicep b/infra/main.bicep
index 96b92e8..cbe78a7 100644
--- a/infra/main.bicep
+++ b/infra/main.bicep
@@ -39,8 +39,23 @@ module resources 'resources.bicep' = {
}
}
+// Deploy budget with alert
+module budget 'budget.bicep' = {
+ scope: rg
+ name: 'budget'
+ params: {
+ budgetName: 'PoFastType-MonthlyBudget'
+ amount: 5
+ actionGroupId: resources.outputs.BUDGET_ACTION_GROUP_ID
+ }
+ dependsOn: [
+ resources
+ ]
+}
+
output AZURE_LOCATION string = location
output AZURE_TENANT_ID string = tenant().tenantId
output WEBSITE_URL string = resources.outputs.WEBSITE_URL
output API_BASE_URL string = resources.outputs.API_BASE_URL
output RESOURCE_GROUP_ID string = rg.id
+output BUDGET_ID string = budget.outputs.budgetId
diff --git a/infra/resources.bicep b/infra/resources.bicep
index 994923d..4877f2a 100644
--- a/infra/resources.bicep
+++ b/infra/resources.bicep
@@ -99,6 +99,45 @@ resource gameResultsTable 'Microsoft.Storage/storageAccounts/tableServices/table
name: 'PoFastTypeGameResults'
}
+// Create Key Vault for secrets
+resource keyVault 'Microsoft.KeyVault/vaults@2023-07-01' = {
+ name: 'pofasttype-kv'
+ location: location
+ tags: tags
+ properties: {
+ sku: {
+ family: 'A'
+ name: 'standard'
+ }
+ tenantId: subscription().tenantId
+ enableRbacAuthorization: true
+ enableSoftDelete: true
+ softDeleteRetentionInDays: 7
+ enabledForDeployment: false
+ enabledForDiskEncryption: false
+ enabledForTemplateDeployment: false
+ publicNetworkAccess: 'Enabled'
+ }
+}
+
+// Create Action Group for budget alerts
+resource budgetAlertActionGroup 'Microsoft.Insights/actionGroups@2023-01-01' = {
+ name: 'PoFastType-budget-alerts'
+ location: 'global'
+ tags: tags
+ properties: {
+ groupShortName: 'BudgetAlert'
+ enabled: true
+ emailReceivers: [
+ {
+ name: 'OwnerEmail'
+ emailAddress: 'punkouter26@gmail.com'
+ useCommonAlertSchema: true
+ }
+ ]
+ }
+}
+
// Create a user-assigned managed identity
resource userAssignedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = {
name: 'PoFastType-identity'
@@ -116,7 +155,7 @@ resource webApp 'Microsoft.Web/sites@2022-03-01' = {
serverFarmId: existingAppServicePlan.id
httpsOnly: true
siteConfig: {
- netFrameworkVersion: 'v9.0'
+ netFrameworkVersion: 'v10.0'
cors: {
allowedOrigins: ['*']
supportCredentials: false
@@ -138,6 +177,36 @@ resource webApp 'Microsoft.Web/sites@2022-03-01' = {
name: 'AzureTableStorage__TableName'
value: 'PoFastTypeGameResults'
}
+ {
+ name: 'AzureKeyVault__VaultUri'
+ value: keyVault.properties.vaultUri
+ }
+ // Enable Application Insights Snapshot Debugger
+ {
+ name: 'APPINSIGHTS_SNAPSHOTFEATURE_VERSION'
+ value: '1.0.0'
+ }
+ {
+ name: 'SnapshotDebugger__IsEnabled'
+ value: 'true'
+ }
+ {
+ name: 'SnapshotDebugger__UploadAllSnapshots'
+ value: 'true'
+ }
+ // Enable Application Insights Profiler
+ {
+ name: 'APPINSIGHTS_PROFILERFEATURE_VERSION'
+ value: '1.0.0'
+ }
+ {
+ name: 'ApplicationInsightsProfiler__IsEnabled'
+ value: 'true'
+ }
+ {
+ name: 'DiagnosticServices__EnableProfiler'
+ value: 'true'
+ }
]
}
}
@@ -154,7 +223,7 @@ resource webAppConfig 'Microsoft.Web/sites/config@2022-03-01' = {
parent: webApp
name: 'web'
properties: {
- netFrameworkVersion: 'v9.0'
+ netFrameworkVersion: 'v10.0'
scmType: 'GitHubAction'
use32BitWorkerProcess: true
webSocketsEnabled: false
@@ -213,3 +282,6 @@ output AZURE_WEB_APP_NAME string = webApp.name
output AZURE_STORAGE_ACCOUNT_NAME string = storageAccount.name
output AZURE_APPLICATION_INSIGHTS_CONNECTION_STRING string = appInsights.properties.ConnectionString
output AZURE_LOG_ANALYTICS_WORKSPACE_ID string = logAnalyticsWorkspace.id
+output AZURE_KEY_VAULT_NAME string = keyVault.name
+output AZURE_KEY_VAULT_URI string = keyVault.properties.vaultUri
+output BUDGET_ACTION_GROUP_ID string = budgetAlertActionGroup.id
From a2454fd9475dd1f8388185f1741c30f7f0263314 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sun, 23 Nov 2025 16:32:24 +0000
Subject: [PATCH 6/6] Fix code formatting and add architecture modernization
summary
Co-authored-by: punkouter26 <121304072+punkouter26@users.noreply.github.com>
---
docs/ARCHITECTURE_MODERNIZATION.md | 238 ++++++++++++++++++++
src/PoFastType.Client/Pages/UserStats.razor | 3 +-
src/PoFastType.Client/wwwroot/index.html | 7 +-
3 files changed, 245 insertions(+), 3 deletions(-)
create mode 100644 docs/ARCHITECTURE_MODERNIZATION.md
diff --git a/docs/ARCHITECTURE_MODERNIZATION.md b/docs/ARCHITECTURE_MODERNIZATION.md
new file mode 100644
index 0000000..4bde9ea
--- /dev/null
+++ b/docs/ARCHITECTURE_MODERNIZATION.md
@@ -0,0 +1,238 @@
+# Architecture Modernization Summary
+
+## Overview
+This document summarizes the comprehensive architecture updates made to the PoFastType application to meet enterprise-grade .NET standards.
+
+## Completed Updates
+
+### 1. Foundation (.NET 10 Migration)
+โ
**Migrated from .NET 9 to .NET 10**
+- Created `global.json` to lock SDK version at 10.0.100
+- Updated all `.csproj` files to target `net10.0`
+- Updated Bicep infrastructure to deploy with .NET 10
+
+โ
**Centralized Package Management**
+- Created `Directory.Packages.props` at repository root
+- Removed version numbers from individual `.csproj` files
+- Includes all major packages: Azure SDKs, OpenTelemetry, Serilog, MediatR, FluentValidation, bUnit, Playwright
+
+โ
**VS Code Integration**
+- Created `.vscode/launch.json` with `serverReadyAction` for F5 debugging
+- Created `.vscode/tasks.json` for build automation
+- Configured one-step debug launch for API and browser
+
+### 2. Repository Structure
+โ
**Reorganized to Standard Folder Structure**
+```
+/src - All source code projects
+/tests - All test projects
+/docs - Documentation, diagrams, coverage reports, KQL queries
+/infra - Bicep infrastructure templates
+/scripts - Helper scripts (Azurite, coverage)
+```
+
+โ
**Documentation Organization**
+- Moved all diagrams to `/docs/Diagrams/`
+- Moved PRD.md to `/docs/`
+- Created `/docs/kql/` with 6 essential monitoring queries
+- Created `/docs/README.md` as documentation index
+- Updated all path references in README.md
+
+### 3. Logging & Observability
+โ
**Serilog Configuration**
+- Configured Serilog to read from `appsettings.json`
+- Separate configurations for Development and Production
+- Structured JSON logging with compact formatter
+- File and console sinks configured
+
+โ
**OpenTelemetry Integration**
+- Added OpenTelemetry with custom metrics support
+- Configured for ASP.NET Core and HTTP client instrumentation
+- Runtime metrics collection enabled
+- Custom meter "PoFastType.Metrics" for business metrics
+
+โ
**Application Insights**
+- Enabled Snapshot Debugger for production debugging
+- Enabled Profiler for performance analysis
+- Configured for Production environment
+
+### 4. Azure Infrastructure (Bicep)
+โ
**Complete Infrastructure as Code**
+- Log Analytics Workspace for centralized logging
+- Application Insights for monitoring and diagnostics
+- Azure Storage Account with Table Storage
+- Azure Key Vault for secrets management
+- User-Assigned Managed Identity
+- Action Group for budget alerts
+
+โ
**Cost Management**
+- Created $5 monthly budget
+- Configured 80% threshold alert
+- Email notifications to punkouter26@gmail.com
+- Deployed via separate `budget.bicep` module
+
+โ
**Security**
+- Azure Key Vault integration (Production only)
+- TLS 1.2 minimum enforced
+- HTTPS only enabled
+- Managed identity for secure access
+- Storage encryption enabled
+
+### 5. Development Environment
+โ
**Local Development**
+- Azurite configured as default storage (Development)
+- Production uses Azure Table Storage
+- Scripts created for starting Azurite (PowerShell & Bash)
+
+โ
**Helper Scripts**
+- `scripts/start-azurite.ps1` & `.sh` - Start Azure Storage Emulator
+- `scripts/run-coverage.ps1` & `.sh` - Run code coverage with reportgenerator
+
+### 6. Monitoring & Diagnostics
+โ
**KQL Query Library**
+Created 6 essential queries in `/docs/kql/`:
+1. `app-performance.kql` - Request metrics and performance
+2. `user-activity.kql` - User engagement tracking
+3. `top-scores.kql` - Leaderboard and performance metrics
+4. `error-analysis.kql` - Exception and failure tracking
+5. `endpoint-performance.kql` - API endpoint performance
+6. `storage-health.kql` - Azure Storage dependency health
+
+### 7. Testing Infrastructure
+โ
**Test Framework Updates**
+- Added bUnit for Blazor component testing
+- Added Playwright for E2E testing
+- Coverage scripts for reportgenerator integration
+- All test packages centrally managed
+
+## Pending Items
+
+### High Priority
+- [ ] Refactor API to Vertical Slice Architecture with `/Features` folder
+- [ ] Convert API from Controllers to Minimal APIs
+- [ ] Ensure PoFastType.Shared contains only DTOs and validation logic
+- [ ] Configure dotnet-coverage with 80% threshold
+- [ ] Update API error handling to RFC 7807 Problem Details
+
+### Medium Priority
+- [ ] Configure GitHub Actions with OIDC/Federated Credentials
+- [ ] Ensure all test methods follow naming convention
+- [ ] Set up automated coverage report generation in CI/CD
+
+## Benefits Achieved
+
+### Performance
+- OpenTelemetry metrics for performance monitoring
+- Application Insights Profiler for bottleneck identification
+- Structured logging for efficient log querying
+
+### Reliability
+- Health checks for dependency monitoring
+- Snapshot Debugger for production issue diagnosis
+- Budget alerts for cost control
+
+### Developer Experience
+- One-step F5 debugging
+- Centralized package management
+- Helper scripts for common tasks
+- Comprehensive KQL query library
+
+### Security
+- Azure Key Vault for secrets (Production)
+- Managed identities for secure access
+- TLS 1.2+ enforcement
+- Storage encryption enabled
+
+### Maintainability
+- Clean folder structure
+- Comprehensive documentation
+- Infrastructure as Code
+- Centralized configuration
+
+## Architecture Patterns
+
+### Applied Patterns
+- **Dependency Injection** - IoC container for all services
+- **Single Responsibility** - Each service has one clear purpose
+- **Dependency Inversion** - Depend on abstractions (interfaces)
+- **Configuration-based** - Serilog, CORS, health checks from config
+
+### Planned Patterns
+- **Vertical Slice Architecture** - Feature-based organization
+- **CQRS with MediatR** - Command/Query separation
+- **FluentValidation** - Declarative validation rules
+- **Problem Details (RFC 7807)** - Standardized error responses
+
+## Technology Stack
+
+### Frontend
+- Blazor WebAssembly
+- Radzen UI Components
+- Bootstrap 5.3
+- ChartJS
+
+### Backend
+- .NET 10
+- ASP.NET Core
+- MediatR (for CQRS when implemented)
+- FluentValidation (for validation when implemented)
+
+### Infrastructure
+- Azure App Service
+- Azure Table Storage
+- Azure Key Vault
+- Application Insights
+- Log Analytics
+
+### DevOps
+- GitHub Actions
+- Azure Developer CLI (azd)
+- Bicep (Infrastructure as Code)
+- Azurite (local development)
+
+## Migration Notes
+
+### Breaking Changes
+- All projects now target .NET 10 (was .NET 9)
+- Folder structure changed - all imports and paths updated
+- Serilog now reads from configuration (was code-based)
+
+### Non-Breaking Changes
+- Added centralized package management
+- Added OpenTelemetry (additive)
+- Added Azure Key Vault support (Production only)
+- Added budget monitoring (Azure only)
+
+## Next Steps
+
+1. **Implement Vertical Slice Architecture**
+ - Create `/src/PoFastType.Api/Features/` folder
+ - Organize endpoints by feature
+ - Implement CQRS with MediatR
+
+2. **Convert to Minimal APIs**
+ - Replace Controllers with Minimal API endpoints
+ - Group endpoints by feature
+ - Add FluentValidation for request validation
+
+3. **Enhance Error Handling**
+ - Implement RFC 7807 Problem Details globally
+ - Add custom problem detail types for domain errors
+ - Ensure all errors return structured responses
+
+4. **Configure CI/CD**
+ - Set up OIDC authentication for GitHub Actions
+ - Implement automated testing with coverage reports
+ - Deploy to Azure on merge to main
+
+## Conclusion
+
+This modernization effort has successfully:
+- Upgraded the application to .NET 10
+- Implemented enterprise-grade observability
+- Established cost controls and monitoring
+- Improved developer experience
+- Enhanced security posture
+- Prepared foundation for architectural improvements
+
+The application is now positioned for continued evolution with a solid foundation of modern .NET practices and Azure cloud-native patterns.
diff --git a/src/PoFastType.Client/Pages/UserStats.razor b/src/PoFastType.Client/Pages/UserStats.razor
index c760e4e..207dfc3 100644
--- a/src/PoFastType.Client/Pages/UserStats.razor
+++ b/src/PoFastType.Client/Pages/UserStats.razor
@@ -169,7 +169,8 @@
private async void OnUserStatsRefreshRequested()
{
- await LoadUserStats(); StateHasChanged();
+ await LoadUserStats();
+ StateHasChanged();
}
private async Task LoadUserStats()
diff --git a/src/PoFastType.Client/wwwroot/index.html b/src/PoFastType.Client/wwwroot/index.html
index 25d3f14..9d0b1db 100644
--- a/src/PoFastType.Client/wwwroot/index.html
+++ b/src/PoFastType.Client/wwwroot/index.html
@@ -3,7 +3,8 @@
- PoFastType
+
+ PoFastType
@@ -26,7 +27,9 @@
An unhandled error has occurred.
Reload
๐
-
+
+
+