- migrator, api, worker were missing 'networks: - internal'
- db and rabbitmq are only on internal network, so services couldn't reach them
- also changed api depends_on db condition to service_healthy
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Base compose: explicit internal network, named volumes with VOLUME_PREFIX
- docker-compose.prod.yml: production images, ports, restart policies, hrynco-services external network on rabbitmq
- docker-compose.Development.yml: cleaned up orphan volumes, named dev volumes
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add HrynCo.NotificationService.Contracts project with SendEmailMessage and NotificationResultMessage
- Add SendEmailConsumer (RabbitMQ worker) with reply-to pattern via CorrelationContext.ReplyTo
- Add SendEmailHandler owning SMTP send + usage increment as business logic
- Add GetChannelUsageSummaryHandler with single DB query via navigation property
- Merge usage stats inline into channels list (daily/monthly with progress bars)
- Refactor AdminChannelsController.Index to use GetChannelUsageSummaryQuery
- Add RabbitMQ service to docker-compose files
- Remove dead AdminChannelUsageController, ChannelUsageViewModel, ChannelUsageSummary
Ref: IT-628
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
MailKit/MimeKit are unnecessary third-party dependencies with known
vulnerabilities. .NET's built-in System.Net.Mail.SmtpClient handles
the same test send without any additional packages.
Ref: IT-628
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Replace CDN bootstrap with local /lib/bootstrap/ - CDN SRI hash was
mismatching and blocking the script entirely (no Bootstrap = no modals)
- Fix addEventListener null error: script runs before @section FormActions
renders the button, so use document-level event delegation instead
Ref: IT-628
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Razor section forwarding across nested layouts is unreliable.
Modal div and script are now directly in Edit.cshtml body (not in
any section) so they are always in the DOM when the page renders.
Button uses addEventListener instead of inline onclick to decouple
from layout rendering order.
Ref: IT-628
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Views are now recompiled on request without needing a full rebuild.
This makes .cshtml changes take effect immediately during development.
Ref: IT-628
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
JsonSerializer.Serialize(p.Settings) uses the compile-time type
EmailChannelSettings, producing only {EmailChannelType:1} in the DB.
Use p.Settings.GetType() to force runtime type so all SmtpChannelSettings
properties (Host, Port, Username, Password, etc.) are actually persisted.
Ref: IT-628
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
autocomplete=off is ignored by Chrome/Firefox when a password field is
present - they clear the username value after page load. Use
autocomplete=new-password on both fields to signal a new-credential
context and prevent autofill interference.
Ref: IT-628
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Use AsNoTracking() on all EmailChannelRepository read methods to prevent
EF identity conflict when Update() attaches a new entity with same key
- Move test modal to @section Scripts rendered at end of <body> so
bootstrap.Modal is available and modal is not nested inside card DOM
- Add @RenderSection('Scripts') forwarding in _EditorLayout to bubble
child scripts sections up to _Layout
- Switch Test button to programmatic bootstrap.Modal() open instead of
data-bs-toggle (more reliable across layout section boundaries)
Ref: IT-628
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
NuGet.Config sets globalPackagesFolder to %USERPROFILE% for Windows.
Docker Linux builds must not inherit this - they use /root/.nuget/packages by default.
Ref: IT-628
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The development static web assets manifest embedded a stale C:\src\ path,
crashing the app on startup via UseStaticWebAssets(). Disabling the manifest
is correct for a containerized service — wwwroot files are still copied to
bin output and served normally via UseStaticFiles().
Ref: IT-628
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- POST /admin/channels/{id}/test — direct SMTP send via MailKit
- Test button shown only on existing channels (not create)
- Bootstrap modal with recipient email input and spinner
- Inline success/error result inside the modal
Ref: IT-628
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- _EditorLayout renders FormActions section outside <form> in the DOM
- Added id='templateForm' to form, form='templateForm' to submit button (HTML5 form association)
- Added migrator env override in docker-compose.Development.yml so connection string is not read from \
Ref: IT-628
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Seq on 5341 conflicts with ItemTracker dev environment
- Dev compose now points to host.docker.internal:5341 (shared Seq)
- Removed seq service and notification_seq volume from dev compose
Ref: IT-628
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Base compose has no ports (env overrides define them)
- Dev override maps api to 5200:8080
- Web Dockerfile updated to reference .Web project name
Ref: IT-628
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- migrations service uses mcr.microsoft.com/dotnet/sdk:10.0
- mounts source, installs dotnet-ef, retries until DB is ready
- api and worker depend on migrator completing successfully
- removed startup migration from Program.cs
Ref: IT-628
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- wwwroot was missing, causing UseStaticFiles warning on startup
- appsettings.Development.json now overrides port to 5433 (Docker dev mapping)
Ref: IT-634
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- docker/environments/docker-compose.yml: base Api + Worker service definitions
- docker/environments/docker-compose.Development.yml: local PostgreSQL + Seq + port mappings
- Solution folder /docker/ in .slnx for IDE access
Ref: IT-628
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Success responses now use ApiResponse<T>{ Success=true, Data=T }
instead of returning raw T — consistent shape for all outcomes.
Ref: IT-628
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- ApiResponse<T>, ApiError in Api/Infrastructure
- ApiControllerBase with IMediator, FromServiceResult, MapServiceError
- EmailTemplatesController: GET list, GET one, POST, PUT, DELETE
- EmailChannelsController: GET list, GET one, POST, PUT, DELETE
Ref: IT-628
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>