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>
- IContextualSerilogLogger<T> / ContextualSerilogLogger<T> in Services/Logging
(handlers get a ForContext<T>-scoped logger via DI, consistent with ItemTracker)
- SerilogRegistrar extension on WebApplicationBuilder (Api)
- SerilogRegistrar extension on HostApplicationBuilder (Worker)
- Both registrars: set Log.Logger, wire Logging + Host/Services, register ILogger singleton
- ServiceCollectionExtensions: register IContextualSerilogLogger<> as transient
- Program.cs in both apps simplified to single builder.AddSerilog() call
Ref: IT-628
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- AppSettings class in Api and Worker with SectionName constant
- appsettings.json: replaced ConnectionStrings section with App section
- Program.cs: bind AppSettings at startup, register as singleton
- Connection string now sourced from AppSettings.ConnectionString
Ref: IT-628
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Rename Template -> EmailTemplate, Provider -> EmailChannel,
ProviderSettings -> EmailChannelSettings, ProviderType -> EmailChannelType,
ProviderUsage -> EmailChannelUsage throughout all layers
- Add Undefined = 0 to EmailChannelType enum for safe default handling
- Remove SaveChangesAsync from EfRepository methods — repositories now only stage changes
- Add SaveChangesAsync to IUnitOfWork and EfUnitOfWork
- Add TransactionBehavior MediatR pipeline: wraps every handler in a transaction,
saves and commits on success, rolls back on exception
- Add MediatR package reference to Services project
Ref: IT-628
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Directory.Packages.props and Directory.Build.props for central package management
- TemplateEntity, ProviderEntity, ProviderUsageEntity (internal to DAL.EF)
- TemplateEntityConfiguration: composite unique index (service_name, key, language_code), Variables as JSON column
- ProviderEntityConfiguration: settings stored as jsonb, index on (service_name, priority)
- ProviderUsageEntityConfiguration: composite unique index (provider_id, date)
- All entities map Id column explicitly as 'id' (snake_case)
- NotificationDbContext with ApplyConfigurationsFromAssembly
Ref: IT-628
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>