Apply NFR improvements: security, reliability, observability, performance, testability - #13
Open
vanespasov wants to merge 14 commits into
Open
Apply NFR improvements: security, reliability, observability, performance, testability#13vanespasov wants to merge 14 commits into
vanespasov wants to merge 14 commits into
Conversation
…ance, testability Security (P0) - Remove hardcoded Bing Maps key; bind via IOptions<BingMapsOptions> and User Secrets - Remove unused key field from WASM client bundle - Restrict AllowedHosts to localhost in production config - Add fixed-window rate limiter (60 req/min) on geocode endpoint - Validate location length in controller (BadRequest >100 chars) Reliability (P1) - Replace blocking .Result Bing call with proper async/await + 10s WaitAsync timeout - Introduce IGeocodingService / BingGeocodingService; controller is now a thin delegate - Replace fragile colon-delimited string response with typed GeocodeResult JSON DTO - Client: Task.WhenAll for parallel validation, loading spinner, user-visible error alert - Wire Countdown.TimerOut to auto-submit the active game on expiry - Null-safe Game.TotalPoints using ?. on every location Observability (P2) - Call app.MapDefaultEndpoints() — /health and /alive now respond - Structured ILogger calls in BingGeocodingService (outcome, cache hit, latency, errors) - Bump OpenTelemetry.Exporter.OpenTelemetryProtocol from 1.9.0 (NU1902) to 1.18.0 - Add GitHub Actions CI workflow (build, unit tests, integration tests, vuln scan) Performance (P3) - Parallelize five per-answer HTTP calls with Task.WhenAll - IMemoryCache on BingGeocodingService keyed by (answer, locationType), TTL 24h Testability and maintainability (P4) - Extract ScoringRules (constants) and BadgeCalculator to FastGeography.Shared - Extract IGeocodingService port and BingGeocodingService adapter - Unit tests: 32 cases for SetCssClass, TotalPoints, ScoringRules, all badge tiers - Integration tests converted to Reqnroll/Gherkin BDD (8 scenarios, fake geocoder) - Fix E2E test selectors; mark Playwright tests as Skip until dev server available - Delete dead template code: HomeController, Counter.razor, SurveyPrompt.razor - Restore @Body routing in MainLayout; Index.razor redirects to /fastgeography - Remove duplicate Client project from AppHost (Client is hosted by Server) UX and Accessibility (P5) - Drop user-scalable=no from viewport meta - Add aria-label on game inputs, role=timer on countdown, aria-live on achievement banner - Add IsChecking parameter to GameRow to disable fields during validation - Add SVG assets: visit.svg and badge-0..9.svg (were missing, causing broken images) Co-authored-by: Cursor <cursoragent@cursor.com>
Orchestrate only FastGeography.Server so the dashboard opens a single origin that serves both the WASM UI and the API. Pass BingMaps__ApiKey from an AppHost secret parameter instead of appsettings. Co-authored-by: Cursor <cursoragent@cursor.com>
…room and joining to it.
…fore a maps service is called.
…and personal history table - Persist all five answer fields (CityAnswer…MountainAnswer) in RoundSubmission for multiplayer rounds, matching the existing solo behaviour (no migration needed) - Track set progress on GameRoom: RoundsCompletedInSet, SetComplete, PlayerSetHistory; StartRound is blocked once 5 rounds are complete - Add StartNewSet hub method (host-only): resets counter and per-player history and broadcasts NewSetStarted so all clients clear their personal tables - Add LeaveRoom hub method: removes the player from the SignalR group, transfers host to the next remaining player, and broadcasts PlayerLeft / HostChanged - Fix OnDisconnectedAsync to broadcast PlayerLeft and trigger host transfer on disconnect - Extend DTOs: RoomStateDto and RoundStartedMessage carry RoundNumber; RoundResultsMessage carries RoundsCompletedInSet and SetComplete; new CompletedRoundRow carries per-player answers for the personal history table - Rewrite MultiplayerGame.razor with a casual-play-style stacked personal table (finished rows via GameRow + live editable row for the current round), Leave Room button in the header, Round N/5 counter, set-total points badge, and Play Another 5 Rounds CTA for the host after the set ends; all-player Round Results table is kept visible between rounds - Add 5 new integration tests covering answer text in results, leave/notify, host transfer on leave, 6th-round rejection, and new-set then round-1 flow Co-authored-by: Cursor <cursoragent@cursor.com>
Nominatim geocoding:
- Accept jsonv2 "category" field as an alias for "class" (FeatureClass computed property)
so type matching works for both json and jsonv2 response formats
- Add addressType-based fallback matching for mountains and rivers so results that carry
addressType but no class/category are still recognised
- Add "volcano" to mountain types (both type and addressType sets)
- Switch User-Agent registration from ParseAdd to TryAddWithoutValidation to avoid parse
failures when the value contains URLs or semicolons; also add Accept: application/json
- Update default and development User-Agent to use the GitHub project URL
- Add integration tests for jsonv2 Category peak, volcano addressType, and river addressType
Leaderboard controller:
- Fix EF Core GroupBy translation error on the weekly filter: project only UserId and
computed Points before ToListAsync, then aggregate in memory
Scoreboard UI:
- Wrap GetFromJsonAsync in try/catch; show an alert on failure instead of a blank page
- Add #blazor-error-ui.show { display: block } to app.css so the Blazor circuit error
banner is visible when the framework sets the "show" class
Co-authored-by: Cursor <cursoragent@cursor.com>
Introduces a curated static catalog of ~400 real toponyms (WellKnownToponyms) covering every playable letter x category for English (A-Z) and Macedonian (A-Sh) where a genuine place exists. Geographic gaps (e.g. no sovereign state starting with X/W in EN, rare Cyrillic letters in MK) are documented in the catalog header rather than invented. Applied two ways: - EF migration SeedWellKnownToponyms: INSERT ON CONFLICT DO NOTHING so existing player-verified rows are never overwritten; Down deletes Provider=Seed. - ToponymSeeder: idempotent runtime seeder after MigrateAsync/EnsureCreatedAsync; no-op on Postgres after the migration runs. Tests: coverage/uniqueness checks for the catalog, integration tests verifying catalog hits (London/Lonon/Vardar) and miss for garbage names, idempotency, and correct Provider tag. SpyGeocodingService extracted to a shared file. Co-authored-by: Cursor <cursoragent@cursor.com>
Introduces IStringLocalizer<UiStrings> across all client pages and components so the sidebar language picker switches the full UI to Macedonian instantly without a page reload. - Added UiStrings.resx (English) and UiStrings.mk.resx (Macedonian) covering ~130 keys: nav, game table, GameRow, ranked, multiplayer lobby/game, scoreboard, login, register, 404, location types, badges - GameLanguageState now sets DefaultThreadCurrentUICulture on language change so IStringLocalizer resolves the correct culture immediately - MainLayout subscribes to GameLanguageState.Changed and uses @key=Language.Code to re-render the active page on switch - Program.cs reads fg_lang from localStorage before RunAsync so the first paint already uses the stored language - Enabled BlazorWebAssemblyLoadAllGlobalizationData and AddLocalization Co-authored-by: Cursor <cursoragent@cursor.com>
…port Generate cached travel stories after valid answers via a pluggable DestinationAi provider (OpenAI, Grok, Claude, Ollama), expose a rate-limited stories API, show a reward panel in all game modes, and add Docker/Aspire production wiring. Co-authored-by: Cursor <cursoragent@cursor.com>
Destination stories are one-shot paragraphs, so Auto now calls OpenAI then local Ollama. Strip Aspire's 10s HTTP resilience handler so those completions are not cancelled mid-request. Co-authored-by: Cursor <cursoragent@cursor.com>
Enable story generation in UI-selected language (EN/MK), decoupling from place verification language. Update DestinationStories.razor to react to language changes and reload stories. Refactor prompt logic for language-specific instructions. Backend now checks place existence in any language. Add unit tests for prompt and backend logic. Expose internals to test project for improved testability.
- Migrated to story pools with multiple angles per place/language - Added ToponymStories table and image fields; DB migrations - Implemented story popovers with place images via new service - Improved game tables with popovers, images, and new icons - Added ScoreboardMe page and deterministic player avatars - Remember last multiplayer room for easy return - Updated APIs to include user ids for avatars - Rate-limited and cached place image lookups - Added unit/integration tests for new features - Updated localization and refactored code for maintainability
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Security (P0)
Reliability (P1)
Observability (P2)
Performance (P3)
Testability and maintainability (P4)
UX and Accessibility (P5)