This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Lunatech Chef is a full-stack meal planning application for Lunatech offices. Users can view upcoming meals and sign up/unsign for them. Admin users can manage offices, dishes, menus, and schedules.
# Build everything (backend + frontend)
gradle buildAll
# Start Postgres + Keycloak containers, then run the app (serves on http://localhost:8080)
gradle devRun
# Run backend only (expects containers already running; this is also the Clever Cloud deploy command)
gradle run
# Run backend tests (requires Docker: testcontainers boots Postgres and Keycloak)
gradle test
# Lint / format Kotlin code
gradle ktlintCheck
gradle ktlintFormat
# Frontend commands (from /frontend directory)
npm ci # Install dependencies
npm start # Dev server on http://localhost:3000
npm run build # Production build
npm test # Run testsIMPORTANT: Clever Cloud builds with buildAll installDist (see clevercloud/gradle.json). At runtime the app is started by the CC_RUN_COMMAND env var on the Clever Cloud app, which runs the installDist start script (build/install/lunatech-chef/bin/lunatech-chef) as a single JVM; the script picks up heap sizing from the platform-provided JAVA_OPTS. The deploy.goal (gradle --no-daemon run) is only the fallback when CC_RUN_COMMAND is unset; running production through Gradle keeps multiple JVMs alive and previously caused OOM instability on the XS instance. Never make run, buildAll, or installDist depend on Docker Compose or tests; dev-only wiring belongs in devRun.
- Backend: Kotlin + Ktor 3.3, Ktorm ORM, PostgreSQL, Flyway migrations
- Frontend: React 18 + Redux Toolkit, React Router, React Bootstrap
- Auth: Keycloak. The frontend attaches the access token as
Authorization: Beareron every request; the backend verifies signature (JWKS), issuer, and audience (jwt.clientId, defaultlunachef, overridable viaJWT_CLIENT_ID) on every call via thekeycloakJWT auth provider.GET /meprovisions first-time users. Admin = the app-specificadminclient role, delivered as a flatrolesclaim by a User Client Role mapper (Token Claim Nameroles, Client ID set, Add to access token ON); thebackofficeandhrmgroups hold that role. Roles are per-client in Keycloak. - Scheduling: Quartz for recurring tasks (auto-schedule creation, monthly reports, Slack lunch reminders)
Application.kt- Main entry point, Ktor server setup with all routesroutes/- HTTP route handlers (Offices, Dishes, Menus, Schedules, Attendances, Users, Reports, etc.)persistence/schemas/- Ktorm ORM entity mappingspersistence/services/- Business logic layerschedulers/- Quartz jobs for recurring schedules, monthly reports, and Slack lunch remindersauth/- Authorization logic and role handlingconfig/- Configuration classes
components/- React components organized by feature (auth/, admin/, shared/)redux/- Redux Toolkit slices for state management (attendance, schedules, offices, users, menus, dishes, reports)App.js- Main app component with routing
- PostgreSQL with Flyway migrations in
src/main/resources/db/migration/ - Local dev:
docker compose up -dstarts Postgres and a Keycloak pre-loaded fromdockerdev/keycloak/lunatech-realm.json(also used as the fixture inKeycloakMeIntegrationTest). Test users:admin.user@lunatech.nl(admin) andnormal.user@lunatech.nl, passwordlunachef.
- Main config:
src/main/resources/application.conf - Local overrides:
src/main/resources/override.conf(create this file, see CONTRIBUTING.md for required variables)
- Create
frontend/.env.developmentandfrontend/.env.productionwith:REACT_APP_BASE_URL=http://localhost:8080 REACT_APP_REALMS_URL=http://localhost:8081/realms/lunatech REACT_APP_CLIENT_ID=lunachef
The requests/ folder contains HTTP client files for testing the API. Requires authentication token setup in http-client.private.env.json (see sample file).
Hosted on Clever-Cloud. Deploy by rebasing production branch on master (fast-forward only):
git checkout production && git pull && git rebase master && git push