aabde17532eb19a91d806dc071b6a7f119fad130
Phase 4 implementation: - 4.1 IONOS SMTP email configuration (production + docker profiles) - 4.2 Portal navigation update (info board, events, forum links) - 4.3 Tier enforcement: PlanTierService (forum=Pro+, info board limits) - 4.4 WebSocket real-time updates (WebSocketEventPublisher) - 4.5 EmailService: notification, event reminder, info board templates + rate limiting - 4.6 Enterprise custom FROM: CustomMailDomain entity, DNS verification, controller New files: - PlanTierService: tier checks for forum/info board/enterprise features - NotificationDispatchService: EMAIL channel dispatch via preferences - WebSocketEventPublisher: STOMP topic push for forum/info board/events - CustomMailDomainService: DNS TXT record verification for custom FROM - MailSettingsController: Enterprise custom domain API endpoints - CustomMailDomain entity + repository - V16 migration: email dispatch index - V17 migration: custom_mail_domains table - Frontend: use-forum-subscription + use-info-board-subscription hooks - Portal navbar: added info board, events, forum navigation items - i18n: added portal nav translations (de + en) Also fixed pre-existing Phase 2.5/3 compilation issues: - Member entity: added userId field - AuditService: added convenience overloads (logEvent, 4-param log) - AuditEventType: added INFO_BOARD_POST_UPDATED, INFO_BOARD_POST_DELETED - QuotaViolationCode: added TIER_UPGRADE_REQUIRED - StaffPermissionChecker: added requirePermission(UserDetails, ...) - TenantContext: added getCurrentTenantId() alias - MemberRepository: added findByUserId, findByClubId, findAllByClubId - EmailServiceTest: updated for new constructor signature
CannaManage
Multi-tenant cannabis club management platform for German Anbauvereinigungen (cultivation associations) under CanG §19.
Overview
CannaManage handles member management, distribution tracking, and legal compliance for cannabis cultivation clubs in Germany. It enforces the strict quotas mandated by the Cannabis Act (CanG) — including monthly limits (50g adult / 30g under-21), daily limits (25g), and THC restrictions for minors.
Tech Stack
| Component | Technology |
|---|---|
| Runtime | Java 21 (Temurin) |
| Framework | Spring Boot 4.0.6 |
| Security | Spring Security 7.0 + JWT (JJWT 0.12.6) |
| ORM | Hibernate 7 / JPA |
| Database | PostgreSQL (prod), H2 (test) |
| Migrations | Flyway 10 |
| API Docs | SpringDoc OpenAPI 2.8.6 |
| Build | Maven (multi-module) |
| Container | Docker Compose (Postgres + app) |
Project Structure
cannamanage/
├── cannamanage-domain/ # JPA entities, enums, TenantContext
├── cannamanage-service/ # Business logic, repositories, ComplianceService
├── cannamanage-api/ # Spring Boot app, controllers, security, DTOs
├── docs/
│ └── sprint-2/ # Sprint planning docs
└── docker-compose.yml # Local dev environment
Modules
cannamanage-domain
JPA entities with multi-tenant isolation via @Filter("tenantFilter"):
Member— club members with age trackingDistribution— cannabis distribution recordsMonthlyQuota— per-member monthly usage trackingBatch/Strain/StockMovement— inventory managementClub— association registrationUser— authentication accounts
cannamanage-service
ComplianceService— CanG §19 quota enforcement (25 unit tests)- Repositories for all entities
cannamanage-api
- Auth — JWT login + refresh token rotation (SHA-256 hashed)
- Members — CRUD for association members
- Distributions — compliance-gated distribution recording
- Stock — batch and inventory management
- Compliance — quota status API
- Multi-tenant isolation via
TenantFilterAspect(Hibernate @Filter activation)
API Endpoints
| Method | Path | Auth | Description |
|---|---|---|---|
| POST | /api/v1/auth/login |
Public | Login with email + password |
| POST | /api/v1/auth/refresh |
Public | Refresh token rotation |
| GET | /api/v1/compliance/quota/{memberId} |
ADMIN, MEMBER | Monthly quota status |
| GET/POST/PUT | /api/v1/members/** |
ADMIN, MEMBER | Member CRUD |
| POST | /api/v1/distributions/** |
ADMIN, MEMBER | Record distributions |
| GET/POST | /api/v1/stock/** |
ADMIN | Stock management |
Swagger UI: http://localhost:8080/swagger-ui.html
Running Locally
# Start PostgreSQL
docker compose up -d
# Run the app
JAVA_HOME=/path/to/jdk-21 ./mvnw spring-boot:run -pl cannamanage-api
# Run all tests (H2 in-memory)
JAVA_HOME=/path/to/jdk-21 ./mvnw clean verify
Testing
- 37 tests total — all green
- 25 unit tests (
ComplianceServiceTest) — quota enforcement logic - 7 integration tests (
AuthControllerIntegrationTest) — full HTTP auth flow - 5 integration tests (
ComplianceControllerIntegrationTest) — quota API with JWT
Integration tests use @SpringBootTest(webEnvironment = RANDOM_PORT) with H2 and Spring's RestClient.
Security Model
- Stateless JWT — no session, no UserDetailsService
- Roles: ADMIN (full access), MEMBER (self-service), STAFF (Sprint 3)
- Multi-tenancy: Hibernate
@Filteractivated per-request via AOP aspect - Refresh tokens: SHA-256 hashed (Spring Security 7 enforces BCrypt 72-byte limit)
- Token rotation on refresh — old tokens invalidated
Sprint History
| Sprint | Focus | Status |
|---|---|---|
| 1 | Domain entities, ComplianceService, 25 tests | ✅ Done |
| 2 | REST API, Spring Security, JWT, OpenAPI, integration tests | ✅ Done |
| 3 | Member portal, STAFF role, real-time notifications | 📋 Planned |
License
Private — Patrick Plate
Description
Languages
Java
61%
TypeScript
38.1%
JavaScript
0.4%
CSS
0.3%
Shell
0.2%