Develop backend#82
Merged
Merged
Conversation
- Fix CA1859/CA2263 analyzer errors in Domain and Application tests - Suppress CA1873 false positives in Directory.Build.props with justification - Add JWT Bearer security definition to Swagger (Authorize button) - Catch RedisException in output-cache middleware to allow startup without Redis - Apply EF migrations to remote dev SQL Server - Seed demo data (categories, news, events, posts, roles)
Bumps all project TFMs from net8.0 to net10.0 and updates package references to compatible versions. - Directory.Build.props: <TargetFramework>net10.0</TargetFramework> - Roslyn source generator remains on netstandard2.0 - All test projects updated to net10.0
- Adds ApplicationErrors with typed error codes (Identity.PASSWORD_RESET, EMAIL_EXISTS, INVALID_CREDENTIALS, etc.) - Introduces Result<T> monad for command/query return types - Adds localization service with YAML-backed stores - Integrates ValidationBehavior and ResultValidationBehavior into MediatR pipeline - DomainException / ConcurrencyException / DuplicateException mapped to problem details via middleware
Replaces coarse-grained domain services (ICommunityWriteService, ICountryProfileService, etc.) with aggregate-specific repository interfaces aligned to DDD per-aggregate persistence. - Introduces I*Repository interfaces per aggregate root - Updates command/query handlers to depend on repositories - Infrastructure layer implements repository interfaces with EF Core - Removes obsolete service abstractions from Application layer
- Added AuditableAggregateRoot for creation and update tracking - Added SoftDeleteAggregateRoot for logical deletion support - Improved domain consistency across aggregates
…y, unify repository commit pattern - IAuthService (Login, RefreshToken, Logout, Register, ForgotPassword, ResetPassword): all 6 auth handlers reduced to thin wrappers (2 deps each instead of 4–6); duplicated token rotation/issuance logic eliminated; wrong ResetPassword domain keys fixed (INVALID_REFRESH_TOKEN → INVALID_RESET_TOKEN, REGISTRATION_FAILED → RESET_FAILED) - Refresh token repos no longer own commits: SaveChangesAsync removed from IRefreshTokenRepository / RefreshTokenRepository — AuthService owns commit via ICceDbContext - DDD hierarchy aligned: Entity<TId> constrained to IEquatable<TId>, domain events moved to AggregateRoot<TId> (inherits SoftDeletableEntity), deleted AuditableAggregateRoot/SoftDeletableAggregateRoot, upgraded 3 entities to AggregateRoot, updated 12 entity base classes - Generic repository pattern: IRepository<T,TId> + Repository<T,TId> base for aggregate repos - Handler commit ownership: IStateRepAssignmentRepository, IExpertRequestSubmissionRepository, IExpertWorkflowRepository, IUserProfileRepository — all SaveChangesAsync calls moved to handl
BREAKING CHANGE: Response.message is now a plain string instead of
{ ar, en } bilingual object, and FieldError.message is also a string.
LocalizationService.GetString() now defaults to
CultureInfo.CurrentUICulture (set by LocalizationMiddleware from
the Accept-Language header) instead of hardcoded "ar".
Changes:
- Response<T>.Message: LocalizedMessage → string
- FieldError.Message: LocalizedMessage → string
- MessageFactory uses _l.GetString() instead of GetLocalizedMessage()
- ExceptionHandlingMiddleware returns single message string
- ResponseValidationBehavior uses GetString() for validation errors
-manage user status - Optimize ListUsersQuery: single-projection with inline role sub-select, replace join-based role filter with EXISTS subquery - Optimize GetUserByIdQuery: collapse two DB round-trips into one Select projection, replace ToList+SingleOrDefault with FirstOrDefaultAsync - Fix auth token handling in user management flow
…ables - Replace bilingual video_url with single field on HomepageSettings - Extract KnowledgePartner as separate aggregate from AboutSettings - Extract PolicySection with PolicySectionType enum from PoliciesSettings - Promote GlossaryEntry, HomepageCountry to AggregateRoot<Guid> - Add order_index to HomepageCountry and all collection tables - Switch all handlers to Response<T> + MessageFactory pattern - Add ICceDbContext.Add/Delete/DeleteRange generic write methods - Add 12 new admin + public endpoints for CRUD (glossary, partners, sections) - Register new repos, EF configs, DI, SystemCode mappings, Resources.yaml keys - Regenerate AddPlatformSettings migration with updated schema (7 tables)
- Add MediaFile entity, EF config, repository, upload options - Add UploadMedia, UpdateMediaMetadata, DeleteMedia, GetMediaById commands/queries - Add MediaFileBriefDto for consistent POST/PUT/DELETE response shape - Add Internal (port 5002) and External (port 5001) REST endpoints - Add MEDIA_UPLOADED/UPDATED/DELETED localized success messages (AR/EN) - Add ERR110-ERR113 error codes for file validation - Create and apply EF migration AddMediaService - Build 0 errors, 773 tests pass (4 pre-existing failures)
- Add Serilog.Sinks.Seq to ship structured logs to Seq - Add OpenTelemetry with ASP.NET Core + HttpClient instrumentation - Export traces to Seq via OTLP (http://localhost:5341/ingest/otlp) - Enrich Serilog logs with TraceId/SpanId from Activity.Current - Register AddCceOpenTelemetry in both External and Internal APIs - Add Seq configuration section to appsettings.json (empty in prod, localhost in dev) - Keep existing Prometheus metrics untouched
- Add `BypassSettings` flag to `NotificationDispatchRequest` so security notifications (password reset) always send regardless of user opt-out - Replace `IPasswordResetEmailSender` with `INotificationGateway` in `AuthService.ForgotPasswordAsync` — sends through the notification system - Add `NotificationChannel.Sms` to password reset dispatch alongside Email - Add `PASSWORD_RESET` SMS template to `ReferenceDataSeeder` (short Arabic and English bodies suitable for SMS character limits) - Fix seeder idempotency check in `SeedNotificationTemplatesAsync` to query by `(code, channel)` instead of deterministic ID, preventing duplicate-key errors on re-seed - Delete unused `IPasswordResetEmailSender` interface and `PasswordResetEmailSender` implementation
…ices Feat/add system notificaton services
- Domain: add OtpVerification aggregate, UserVerification entity, OtpVerificationType enum - Application: add RequestVerification and VerifyOtp commands, handlers, validators, DTOs - Application: add IOtpVerificationRepository, IUserVerificationRepository, IUserRepository, IOtpCodeGenerator - Application: add OTP system codes (ERR120–ERR125, CON060–CON061) and message factory shortcuts - Infrastructure: add EF configurations, repository implementations, HMAC-based OtpCodeGenerator - Infrastructure: register verification services and UserRepository in DI - API: add POST /verification/request and POST /verification/verify endpoints (anonymous) - Seeder: add OTP_VERIFICATION notification template for Email and SMS channels
…rmation - Domain: add OtpVerification aggregate, UserVerification entity, OtpVerificationType enum - Application: add RequestVerification and VerifyOtp commands/handlers/validators/DTOs - Application: add IUserRepository, IOtpVerificationRepository, IUserVerificationRepository, IOtpCodeGenerator - Application: add OTP system codes (ERR120–ERR125, CON060–CON061) and message factory shortcuts - Application: refactor VerifyOtpCommandHandler to use repositories instead of direct ICceDbContext queries - Infrastructure: add EF configurations, repository implementations, HMAC-based OtpCodeGenerator - Infrastructure: add UserRepository with FindUserIdByContactAsync and StampConfirmedAsync - Infrastructure: fix ConcurrencyStamp null stub causing DbUpdateConcurrencyException on user confirm - Infrastructure: restore pre-existing missing sub-namespace usings in DependencyInjection.cs - Infrastructure: fix missing using in MessagingOptions.xml doc cref - API: add POST /verification/request and POST /verification/verify endpoints (anonymous) - API: add Otp:HmacSecret to appsettings (dev + base configs) - Seeder: add OTP_VERIFICATION notification template - Migration: AddOtpVerification
Feat/add otp verification
add-roles claims add-users-claims
- Make POST /api/community/polls/{id}/vote idempotent (upsert):
delete existing votes for the user before inserting new ones,
allowing vote changes without conflict
- Add PollOption.DecrementVotes() for consistent counter rollback
- Add IPollRepository.RemoveVotesAsync(); remove HasVotedAsync
- Add PollSummaryDto / FeedPollOptionDto — vote counts, percentages,
userVoted, resultsVisible computed per request
- Extend FeedHydratorService with poll batch fetch + user voted IDs
- Extend GetPublicPostByIdQueryHandler and
ListPublicPostsInTopicQueryHandler to load poll data
- Add PollSummaryDto? Poll to CommunityFeedItemDto, PublicPostDto,
PostDetailDto
- Add audit fields (CreatedOn, CreatedById, LastModifiedOn,
LastModifiedById) to User entity + migration AddUserAuditFields
- Update all User factory methods to accept ISystemClock
- Extend CommunityUserProfileDto with ExpertBioAr, ExpertBioEn,
CountryNameAr, CountryNameEn, JoinedDate
- Change profile endpoint to AllowAnonymous
Feat add claims based matrix db + community audits
…es counters per user
…ignal-r feat/enhance signal r
…ardening Phase 1 — Envelope correctness (no contract change) - ExceptionHandlingMiddleware: locale resolved from Accept-Language header - UnauthorizedAccessException maps to 401; correlationId added to envelope body - BAD_REQUEST message made distinct from VALIDATION_ERROR - General.NOT_FOUND renamed to RESOURCE_NOT_FOUND_GENERIC - GetShareLinkQueryHandler consumer fixed - 6/6 middleware tests pass Phase 2 — Contract changes + handler migration - DomainException → 422 BUSINESS_RULE_VIOLATION (ERR910) - Rate-limit 429 envelope via EnvelopeWriter (replaced External plain-text, added Internal OnRejected) - JWT 401/403 envelope via OnChallenge/OnForbidden - EnvelopeWriter, EnvelopeResult, EnvelopeResults created as single serialization/status-mapping source of truth - ~40 handlers migrated to Response<T>; all endpoint files processed Phase 3 — Dead Result<T> deletion - Deleted: Result.cs, Error.cs, OkApiResult.cs, CreatedApiResult.cs, NoContentApiResult.cs, ResultExtensions.cs, ResultValidationBehavior.cs, ValidationBehavior.cs - All source projects build Phase 4 — Cleanup and hardening - ApplicationErrors → DomainKeys; moved Errors/ → Messages/; namespace CCE.Application.Errors → CCE.Application.Messages - Bulk-replaced 193 ApplicationErrors. references and 51 using directives - Hellang ProblemDetails dependency removed - LoggingBehavior + ValidationBehavior registrations removed - CSP hardened: X-Frame-Options, base-uri, form-action, object-src added - Fixed middleware order comment in External/Program.cs Phase 5 — Build integrity + DomainKeys completeness - Added DomainKeysIntegrityTests (3 tests) in CCE.ArchitectureTests: bidirectional consistency between DomainKeys and SystemCodeMap — all pass - Fixed missing using Microsoft.Extensions.Configuration in EnvelopeWriter.cs - Filled 33 orphaned SystemCodeMap entries as new DomainKeys constants (General, Identity, Content, Verification, Validation, new InterestTopic class) - Added 5 missing SystemCodeMap entries + SystemCode constants (ERR032, CON073–CON076) Phase 6 — Validator hardening + SystemCodeMap completion (code review fixes) - Add .WithErrorCode(MessageKeys.Validation.*) to Login, ForgotPassword, RefreshToken, Logout, UpdateMyProfile, CreateUser, SubmitExpertRequest validators — all were emitting ERR900 on validation failure - Replace all remaining .WithMessage() with .WithErrorCode() - RegisterUserCommandValidator: add RuleLevelCascadeMode = CascadeMode.Stop to prevent double REQUIRED_FIELD + INVALID_FORMAT on empty fields; add .NotEmpty() guards before Password Must() and ConfirmPassword Equal() - Rename MatchStoryPasswordPolicy → MatchStrongPasswordPolicy (typo) - ResetPasswordCommandValidator: add .NotEmpty() guards on NewPassword and ConfirmPassword; update method reference to renamed policy check - Add ERR410 ROLE_NOT_FOUND, ERR411 INVALID_RESET_TOKEN, ERR412 EMAIL_CHANGE_FAILED, ERR127 OTP_UNAUTHORIZED to SystemCode + SystemCodeMap + Resources.yaml - Add CON077–CON082 claims/permissions grant/revoke/update; CON083 AD_LOGIN_SUCCESS; CON084 NEWSLETTER_SUBSCRIBED; CON085 TOPICS_LISTED; CON086 SECTION_REORDERED — all branch feature keys that were falling back to ERR900 - ExceptionHandlingMiddleware: remove ex.Message fallback from UnauthorizedAccessException to prevent internal message leakage - ListRedisKeysQuery: replace raw "ITEMS_LISTED" with MessageKeys.General.ITEMS_LISTED All source projects build with 0 errors, 0 warnings.
…ll pattern Deleted all 52 convenience shortcut methods from MessageFactory, leaving exactly 9 core methods (Ok, NotFound, Conflict, Unauthorized, Forbidden, BusinessRule, ValidationError, Field). Updated ~130 handlers across CCE.Application to call the generic API directly with explicit MessageKeys constants. Previously 83% of handlers used generic calls while 17% used shortcuts, forcing every reader to check whether a shortcut exists before writing a handler. Three handlers already mixed both styles. The factory now has one pattern with no exceptions. Rule going forward: new outcomes add a MessageKeys constant, a SystemCodeMap entry, and a Resources.yaml string — nothing else touches MessageFactory.
…fied--Response--migration,-and-validation-hardening Feat/unified error envelope, unified response migration, and validation hardening
Adds push as a first-class notification channel alongside email and in-app,
following the existing INotificationChannelHandler plug-in model.
- UserDeviceToken entity: one row per (UserId, DeviceId); token rotates,
deviceId is stable. EF migration: AddUserDeviceToken
- POST /api/me/device-tokens — register/refresh FCM token (upsert by deviceId)
- DELETE /api/me/device-tokens/{deviceId} — unregister
- PushNotificationChannelSender: fetches active tokens, sends FCM multicast,
deactivates stale tokens on UNREGISTERED/INVALID_ARGUMENT errors
- FirebaseMessagingService wraps FirebaseAdmin SDK with singleton guard
(DefaultInstance ?? Create) and forwards CancellationToken
- Firebase section in appsettings; channel skipped cleanly when unconfigured
- MetaData added to RenderedNotification so handlers can pass deep-link
context (postId, communityId, etc.) into FCM data payload
- NotificationChannel.Push added to all existing notification handlers
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
feat: Firebase FCM push notification channel
Feat/cce reports
…y/feed Adds optional ?searchTerm= parameter to the community feed endpoint that routes through Meilisearch instead of the SQL feed path when non-empty. Results share the same CommunityFeedItemDto shape, extended with four nullable highlight fields (TitleHighlight, BodyHighlight, MatchedInReply, ReplyExcerpt). When searchTerm is absent the endpoint is unchanged. Key changes: - New SearchCommunityPostsQuery / validator / handler; relevance path reorders by Meilisearch rank in memory, sort path delegates to SQL ORDER BY over the candidate ID set; both delegate hydration to the existing FeedHydratorService - Two new Meilisearch indexes: community_posts and community_replies; backfilled on Internal API startup and kept live via PostCreated / ReplyCreated domain-event handlers - StripUnknownSearchFieldsHandler DelegatingHandler strips rankingScoreThreshold (SDK v0.15.5 default 0.0) that Meilisearch ≤v1.7 rejects; harmless on newer versions - Global /api/search catch widened from MeilisearchApiError to System.Exception to match the SDK's actual throw types against cloud - Prometheus metrics: community_search_hits, community_search_duration_ms, community_search_meili_failures - Meilisearch config wired to hosted cloud instance in both API appsettings - Search folder reorganised: Application/Search/Dtos/ and Infrastructure/Search/Documents/ subfolders; inline classes extracted from MeilisearchClient into their own files
Feat/add mention feature
There was a problem hiding this comment.
Semgrep OSS found more than 20 potential problems in the proposed changes. Check the Files changed tab for more details.
| const line = document.createElement('div'); | ||
| line.className = 'log-entry'; | ||
| const typeClass = type === 'error' ? 'log-error' : type === 'info' ? 'log-info' : type === 'event' ? 'log-event' : 'log-payload'; | ||
| line.innerHTML = '<span class="log-time">[' + time + ']</span> <span class="' + typeClass + '">' + eventName + '</span>' + (payload ? ' ' + syntaxHighlight(payload) : ''); |
| const line = document.createElement('div'); | ||
| line.className = 'log-entry'; | ||
| const typeClass = type === 'error' ? 'log-error' : type === 'info' ? 'log-info' : type === 'event' ? 'log-event' : 'log-payload'; | ||
| line.innerHTML = '<span class="log-time">[' + time + ']</span> <span class="' + typeClass + '">' + eventName + '</span>' + (payload ? ' ' + syntaxHighlight(payload) : ''); |
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.
Summary
Backend enhancements