Patrick Plate 8490da4705
Deploy to Production / test (push) Has been cancelled
Deploy to Production / deploy (push) Has been cancelled
fix(api): add spring-boot-starter-flyway for Spring Boot 4 migrations
Backend crashed on startup with 'Schema validation: missing table
[audit_events]'. Root cause: this is Spring Boot 4.0.6, which modularized
autoconfiguration. FlywayAutoConfiguration moved out of spring-boot-autoconfigure
into a dedicated spring-boot-flyway module that is only pulled in by
spring-boot-starter-flyway. The pom only had flyway-database-postgresql
(+ transitive flyway-core) but NOT the starter, so spring.flyway.enabled=true
was inert: no migrations ran, flyway_schema_history was never created, and
Hibernate ddl-auto=validate failed on the empty schema.

Adds spring-boot-starter-flyway (autoconfigure module + flyway-core); keeps
flyway-database-postgresql for the Postgres dialect.
Ref: https://spring.io/blog/2025/10/28/modularizing-spring-boot/
2026-06-13 09:52:22 +02:00

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 tracking
  • Distribution — cannabis distribution records
  • MonthlyQuota — per-member monthly usage tracking
  • Batch / Strain / StockMovement — inventory management
  • Club — association registration
  • User — 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 @Filter activated 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

S
Description
CannaManage — B2B SaaS for German Cannabis Social Clubs (Anbauvereinigungen)
Readme 7.8 MiB
Languages
Java 61%
TypeScript 38.1%
JavaScript 0.4%
CSS 0.3%
Shell 0.2%