Skip to content

Feature: straightmail v0.5.0#4

Open
codeministry wants to merge 27 commits into
encircle360-oss:masterfrom
codeministry:feature/straightmail-v0.5.0
Open

Feature: straightmail v0.5.0#4
codeministry wants to merge 27 commits into
encircle360-oss:masterfrom
codeministry:feature/straightmail-v0.5.0

Conversation

@codeministry

@codeministry codeministry commented May 29, 2026

Copy link
Copy Markdown
Member

feat: straightmail v0.5.0 — multi-tenancy, SQLite, API-key auth, Git-sync, four Docker stacks

Summary

This PR introduces multi-tenancy, three authentication modes, Git-sync for templates, SQLite as the default database,
and a restructured Docker Compose setup. Changes span backend, frontend, infrastructure, and CI/CD.


What's new

Multi-tenancy

  • Per-tenant SMTP configuration (host, port, TLS, credentials, sender address)
  • Per-tenant API keys stored as SHA-256 hashes
  • Per-tenant Git-Sync with individual repository URL and access token
  • TenantReconciliationService: tenants declared in application.yml under tenants.config.* are automatically
    created or updated at startup — zero-touch provisioning, no manual API calls required
  • New REST API: GET/POST/PUT/DELETE /api/v1/tenants (admin-only, requires database profile)
  • TenantContext resolves the active tenant from the request on every call; service and business logic layers never
    read HttpServletRequest directly

Authentication modes (AUTH_MODE)

The boolean auth.enabled flag has been replaced by a three-value auth.mode property:

AUTH_MODE Behaviour
oidc (default) JWT Bearer token from OIDC provider; tenant resolved from configurable JWT claim
api-key X-API-KEY header; per-tenant or global SHA-256 hash lookup
none All endpoints open; suited for network-isolated deployments

Git-Sync for templates

  • Per-tenant Git repository cloned on a configurable cron schedule
  • ShedLock prevents concurrent sync in HA deployments
  • GitTemplateSourceProvider loads .ftl files from the working tree into a local cache table
  • Sync status and last-run result visible in the admin dashboard

Template source architecture

Templates are now resolved from up to three independent sources; the first match wins:

  1. Database — full CRUD via UI / API (requires database profile)
  2. File — read-only .ftl files from a mounted host directory
  3. Git-Sync — cloned from a per-tenant Git repository on a schedule

Every template carries an implicit source:database, source:file, or source:git tag — filterable via the API.

Encryption at rest

EncryptionService (AES-256-GCM) encrypts all secrets before persistence (SMTP password, Git token, API key hash).
Secrets are never returned in plain text by the API. Double-encryption is prevented by checking the stored value before
each write.

SQLite as default database

SQLite (WAL mode, single-file) replaces PostgreSQL as the default. PostgreSQL remains fully supported via environment
variables:

DB_URL=jdbc:postgresql://host:5432/straightmail
DB_DRIVER=org.postgresql.Driver
DB_DIALECT=org.hibernate.dialect.PostgreSQLDialect
DB_USERNAME=...
DB_PASSWORD=...

Frontend changes

  • Tenant management UI — full CRUD for tenants with SMTP configuration form, API key management, and Git-Sync
    settings
  • API-Key login page — alternative login flow for AUTH_MODE=api-key deployments
  • Dashboard — new Git-Sync status card and File-Sync status card alongside the existing API status card
  • New NGXS states: TenantState, ApiKeyState
  • New guards: adminGuard (tenant admin area), canDeactivateFormGuard (unsaved changes protection)
  • New interceptor: ApiKeyInterceptor adds X-API-KEY and X-Tenant-ID headers in API-Key mode

Infrastructure changes

Docker Compose

The single root-level docker-compose.yml has been replaced by four mode-specific stacks in docker/:

Stack Auth Database Use case
minimal.yml None Quick local testing, file-based templates only
apikey-sqlite.yml API-Key SQLite Lightweight self-hosted deployment
oidc-sqlite.yml OIDC + Keycloak SQLite Full multi-tenant development setup
oidc-postgres.yml OIDC + Keycloak PostgreSQL Production-equivalent local environment

Build the JAR first, then start a stack:

cd backend && ./gradlew bootJar
cd ../docker && docker compose -f oidc-sqlite.yml up

See docker/README.md for details.

Helm chart

The Helm chart has been removed from this repository and will be maintained in a dedicated helm-charts repository.


Developer experience

  • application-local.yml is now gitignored — real credentials can never be accidentally committed
  • application-local.yml.example added with placeholder values (ACME tenant, smtp.example.com)
  • application-dev.yml removed; use application-local.yml (copied from the example) instead
  • SKIP_FRONTEND_BUILD=true skips the Angular build for backend-only development

Breaking changes

Upgrading from v0.4.0? The changes below require manual migration steps before deploying this version. Review each
item carefully.

What changed Before After
Auth config key auth.enabled: true/false AUTH_MODE: oidc / api-key / none
Frontend directory client/ frontend/
Docker Compose docker-compose.yml at root Four stacks in docker/
Template service package service/template/TemplateService (monolithic) service/template/provider/ (composite providers)
Controller package controller/MailController (flat) controller/mail/MailController (nested)
Default database PostgreSQL (MongoDB removed in v0.4.0) SQLite (PostgreSQL still supported via env vars)
ENCRYPTION_KEY Optional Required in database profile
Liquibase migrations Single changeset file Consolidated master file with 8 changesets
Liquibase 0001 type uuid (PostgreSQL native) varchar(36) — requires manual migration on v0.4.0 PostgreSQL instances

Liquibase note

Changeset 0001 changes the id column type from uuid to varchar(36) for SQLite compatibility.

Existing PostgreSQL deployments (v0.4.0) are affected. If the old 0001 changeset has already been applied (which it has on any v0.4.0 installation), Liquibase will detect a checksum mismatch and refuse to start. Before applying this PR to a v0.4.0 PostgreSQL instance, either:

  1. Add a compensating changeset that alters the id column type from uuid to varchar(36), or
  2. Clear the Liquibase checksum for changeset 0001 in the databasechangelog table and ensure the column type is compatible.

Test coverage

  • All existing tests updated for the new architecture
  • MailHog replaced by Mailpit in Docker Compose stacks and Testcontainers
  • New test classes: TenantIntegrationTest, TenantSenderEnforcementTest, TemplatesControllerTest,
    StatusControllerTest, EncryptionServiceTest, TenantServiceTest, TenantReconciliationServiceTest,
    GitSyncSchedulerTest, and more
  • Backend unit tests (non-Docker) pass locally; Docker-dependent integration tests (Mailpit Testcontainer) run in CI

Changelog

Backend — [0.5.0]

Added

  • Multi-tenancy: TenantService, TenantRepository, /v1/tenants CRUD admin API
  • Per-tenant API key authentication (ApiKeyTenantResolutionFilter) with SHA-256 hashing via EncryptionService
  • AbstractTenantResolutionFilter base class; JWT and API-key resolution share common logic
  • Config-based tenant provisioning via TenantProperties + TenantReconciliationService (YAML-declared tenants
    reconciled at startup)
  • StartupConfigLogger: logs all resolved configuration at startup with masked secrets
  • auth.mode property (oidc | api-key | none) replacing the boolean auth.enabled flag
  • No-authentication mode: NoAuthTenantResolutionFilter resolves tenant from X-Tenant-ID header or falls back to
    default
  • Database-backed Git sync status (GitSyncStatusRepository) and template caching
  • GitSyncScheduler with initial sync on application startup; ShedLock uses JVM system time for SQLite compatibility
  • Read-only template view (TemplateView.editable) distinguishing DATABASE vs FILE/GIT templates
  • Implicit source tags (source:database, source:file, source:git) auto-applied by TemplateService
  • Branch-aware Git template handling: multiple branches can expose the same template ID
  • effectiveTags unifying explicit and source tags for consistent tag-based filtering
  • Tenant branding fields (logo_url, brand_color) in TenantDTO
  • Comprehensive Javadoc on all public REST controller methods

Changed

  • Switched default database from PostgreSQL to SQLite (jdbc:sqlite:); Liquibase changesets updated
  • MailHog replaced by Mailpit in Testcontainers and all Docker Compose stacks
  • Keycloak hostname handling updated; JWK endpoint URL and issuer URL split into separate config properties
  • ObjectProvider<T> used for all beans conditional on the database profile
  • Package structure reorganized: controller/mail/, controller/template/, controller/tenant/,
    service/template/loader/, service/template/provider/
  • @Autowired field injection replaced by constructor injection throughout

Removed

  • auth.enabled boolean property (superseded by auth.mode)
  • Helm chart and related CI workflows

Frontend — [0.5.0]

Added

  • Multi-tenancy UI: tenant list, create/edit/delete forms, tenant selector in sidebar
  • API-key authentication mode: /api-key-login page, ApiKeyState, ApiKeyInterceptor
  • TenantState.isDatabaseMode selector; DB-mode-only actions (New Template, New Tenant) conditionally rendered
  • TemplatesState with source-tab filtering (DATABASE / FILE / GIT / ALL) and tag filtering
  • TemplateGridComponent and grid/list view toggle (persisted in UiState)
  • Read-only TemplateViewComponent for GIT/FILE templates
  • Git branch badge in template table/grid for source:git templates
  • DashboardState tracking Git sync status per tenant
  • FileSyncCard / GitSyncCard components for sync status display
  • UserAvatar, TenantAvatar, NavIconComponent reusable UI components
  • HelpPanelComponent: context-sensitive off-canvas help panel (right panel / bottom sheet on mobile)
  • Auto-save for render form values (persisted in LocalStorage via RenderState)
  • Playwright E2E test suites: templates.spec.ts, tenants.spec.ts
  • autoAuth Playwright fixture: pre-seeds sessionStorage/localStorage before each test

Changed

  • TranslateModule imports replaced by standalone TranslatePipe across all components
  • MailHog references replaced with Mailpit in E2E test configurations
  • Unsaved-changes guard unified via canDeactivate across form pages
  • Font sizes standardized to rem units across all component stylesheets

Fixed

  • mockTenants E2E helper now mocks /v1/tenants/me (the actual LoadTenants endpoint) instead of /v1/tenants
  • Playwright autoAuth fixture seeds tenant with editable: true so "New Template" / "New Tenant" buttons render
    correctly

Checklist

  • Credential scan: zero matches for real SMTP hosts, passwords, tokens, or customer names
  • application-local.yml gitignored
  • All four Docker Compose stacks validate (docker compose config)
  • Backend compiles without errors (./gradlew compileJava -PskipFrontendBuild=true)
  • Backend unit tests pass locally
  • No NgModule created; all Angular components standalone
  • No detectChanges() calls in production Angular code

@codeministry codeministry marked this pull request as draft May 29, 2026 10:13
@codeministry codeministry marked this pull request as ready for review May 29, 2026 10:14
@codeministry codeministry changed the title Feature/straightmail v0.5.0 Feature straightmail v0.5.0 May 29, 2026
@codeministry codeministry changed the title Feature straightmail v0.5.0 Feature: straightmail v0.5.0 May 29, 2026
- Add tenant configuration instructions (`TENANTS_CONFIG`) for non-database profiles.
- Document breaking changes for template directory structure and tenant subdirectories.
- Update Docker Compose examples with new environment variables and volume mappings.
# Conflicts:
#	backend/src/test/java/com/encircle360/oss/straightmail/AbstractTemplateLoaderTest.java
#	src/main/java/com/encircle360/oss/straightmail/service/EmailService.java
#	src/main/java/com/encircle360/oss/straightmail/service/template/AbstractTemplateLoader.java
…rove error logging functionality

- Added AbstractTemplateLoaderTest to validate template loading scenarios, covering missing and partial templates.
- Enhanced error logging in `AbstractTemplateLoader` to differentiate required vs optional template file handling.
- Improved `EmailService` logging to provide better operator visibility for email requests and SMTP dispatch events.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant