Update all references to match the new repo-root Gradle layout after moving the wrapper out of backend/. - Quick Start: add ./gradlew up alternative and hint at ./gradlew check - Spring profiles: ./gradlew bootRun → ./gradlew :backend:bootRun - Development section: add All-in-one subsection with check/up/down/reset - Backend dev: cd backend && ./gradlew bootRun → ./gradlew :backend:bootRun - Development vs Production table: ./gradlew bootRun → ./gradlew :backend:bootRun - Project Structure tree: add gradlew, gradle/, settings.gradle, build.gradle - Remove ARCHITECTURE.md reference (file never existed) - Add Database reset section with ./gradlew reset Also add .gradle/ and build/ to .gitignore with gradle-wrapper.jar exception (was staged but not committed with previous refactor).
8.3 KiB
8.3 KiB
BilHej / Bilhälsning.se
Send a physical letter to a Swedish car owner — just by knowing their license plate.
The user enters a registration number, composes a letter (from a template or free text), pays, and BilHej handles the rest: owner address lookup via Transportstyrelsen, printing and mailing via PostNord. The sender never sees the recipient's name or address.
Tech Stack
| Layer | Technology |
|---|---|
| Frontend | Vue.js 3 (Composition API), Vite, Pinia |
| Backend | Java 21, Spring Boot 4, Gradle |
| Database | PostgreSQL 16 |
| Auth | Spring Security + JWT |
| Payments | Stripe (cards + Swish) |
| Deployment | Docker, Docker Compose |
Prerequisites
- Docker & Docker Compose
- Java 21 (for local IDE development)
- Node.js 20+ (for local frontend dev)
- A Stripe account (test mode for development)
Quick Start
git clone <repo-url> bilhej
cd bilhej
cp .env.example .env # fill in your keys
docker compose up -d # or: ./gradlew up
The app will be available at:
- Frontend:
http://localhost:3000 - Backend API:
http://localhost:8080 - PostgreSQL:
localhost:5432
Architecture inside Docker Compose
Browser Docker Compose network
─────── ─────────────────────
│ ┌──────────────────┐
│ http://localhost:3000 │ frontend (Vite) │
├────────────────────────→│ :3000 │
│ │ proxy: /api → │
│ GET /api/orders │ backend:8080 │
│ └────────┬─────────┘
│ │
│ ┌────────▼─────────┐
│ │ backend (Spring) │
│ │ :8080 │
│ │ profile: docker │
│ └────────┬─────────┘
│ │
│ ┌────────▼─────────┐
│ │ postgres (16) │
│ │ :5432 │
│ └──────────────────┘
Vite proxy: The Vite dev server proxies /api/* requests to the backend container.
No CORS configuration needed in development — the browser never calls the backend directly.
Spring profiles:
| Profile | Datasource | Use |
|---|---|---|
| default | H2 in-memory | Local IDE dev (./gradlew :backend:bootRun) |
docker |
PostgreSQL via docker-compose.yml |
Docker Compose dev |
prod |
PostgreSQL (production config) | Deploy (docker-compose.prod.yml) |
Environment Variables
Copy .env.example to .env and fill in:
| Variable | Description |
|---|---|
POSTGRES_DB |
Database name (default: bilhej) |
POSTGRES_USER |
Database user |
POSTGRES_PASSWORD |
Database password |
JWT_SECRET |
Secret key for JWT signing |
STRIPE_SECRET_KEY |
Stripe secret key |
STRIPE_WEBHOOK_SECRET |
Stripe webhook signing secret |
STRIPE_PRICE_ID |
Stripe price ID for single letter |
Project Structure
bilhej/
├── frontend/ # Vue.js 3 SPA
│ ├── src/
│ │ ├── components/ # Reusable UI components
│ │ ├── composables/ # Shared composition functions
│ │ ├── layouts/ # Page layouts
│ │ ├── pages/ # Route-level page components
│ │ ├── router/ # Vue Router config
│ │ ├── stores/ # Pinia stores
│ │ ├── api/ # API client and endpoints
│ │ ├── assets/ # Static assets, CSS
│ │ ├── App.vue
│ │ └── main.ts
│ ├── index.html
│ ├── vite.config.ts
│ └── package.json
├── backend/ # Spring Boot 4
│ ├── src/main/java/se/bilhalsning/
│ │ ├── BilHejApplication.java
│ │ ├── config/ # Security, CORS, Stripe config
│ │ ├── controller/ # REST controllers
│ │ ├── dto/ # Data transfer objects
│ │ ├── entity/ # JPA entities
│ │ ├── repository/ # Spring Data repositories
│ │ ├── service/ # Business logic
│ │ └── security/ # JWT filter, user details
│ └── src/main/resources/
│ ├── application.yml # default profile (H2)
│ ├── application-docker.yml # docker profile (PostgreSQL)
│ └── db/migration/ # Flyway migrations
├── docker-compose.yml # dev: postgres + backend (bootRun) + frontend (Vite HMR)
├── docker-compose.prod.yml # prod: multi-stage builds, no source mounts, restart: unless-stopped
├── docker/
│ ├── backend.Dockerfile # dev: JDK + gradle :backend:bootRun
│ ├── backend.prod.Dockerfile # prod: multi-stage (Gradle → JRE Alpine, non-root)
│ ├── frontend.Dockerfile # dev: Node + vite dev server
│ ├── frontend.prod.Dockerfile # prod: multi-stage (Node → nginx)
│ ├── nginx.conf # prod: SPA fallback + /api proxy
│ └── entrypoint.sh # prod: self-signed cert generation
├── gradlew # Gradle wrapper (run from repo root)
├── gradle/
│ └── wrapper/
├── settings.gradle # rootProject.name + include 'backend'
├── build.gradle # convenience tasks: check, up, down, reset
├── .env.example
├── README.md
├── REQUIREMENTS.md
└── CODING_GUIDELINES.md
Development vs Production
| Aspect | docker compose up -d |
docker compose -f docker-compose.prod.yml up -d |
|---|---|---|
| Backend | ./gradlew :backend:bootRun (compiles on change) |
Multi-stage build → java -jar app.jar |
| Backend image | eclipse-temurin:21-jdk (~400 MB) |
eclipse-temurin:21-jre-alpine (~200 MB) |
| Backend user | root | bilhej (non-root) |
| Frontend | Vite dev server (HMR, --host 0.0.0.0) |
nginx serving static dist/ |
| API proxy | Vite built-in proxy (/api → backend:8080) |
nginx proxy_pass |
| SSL | None | Self-signed cert auto-generated on first start (.certs/ volume) |
| Source mounts | Yes (live edit) | No (files baked into image) |
| Restart policy | Manual | unless-stopped |
| Purpose | Write code, instant feedback | Verify production build |
Development
All-in-one (from repo root)
./gradlew check # lint → frontend test → backend test → integration test
./gradlew up # docker compose up -d
./gradlew down # docker compose down
./gradlew reset # docker compose down -v && docker compose up -d (full DB reset)
Frontend (dev server with HMR)
cd frontend
npm install # first time only
npm run dev # :3000 with HMR
Backend (IDE or CLI)
./gradlew :backend:bootRun # :8080, profile: default (H2)
Stripe Webhooks (local testing)
stripe listen --forward-to localhost:8080/api/webhooks/stripe
Database reset
./gradlew reset # wipes DB volume and restarts containers
Related Documents
- REQUIREMENTS.md — Full product requirements and business model
- CODING_GUIDELINES.md — Code conventions and standards