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).
210 lines
8.3 KiB
Markdown
210 lines
8.3 KiB
Markdown
# 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](https://stripe.com) account (test mode for development)
|
|
|
|
---
|
|
|
|
## Quick Start
|
|
|
|
```bash
|
|
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)
|
|
|
|
```bash
|
|
./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)
|
|
|
|
```bash
|
|
cd frontend
|
|
npm install # first time only
|
|
npm run dev # :3000 with HMR
|
|
```
|
|
|
|
### Backend (IDE or CLI)
|
|
|
|
```bash
|
|
./gradlew :backend:bootRun # :8080, profile: default (H2)
|
|
```
|
|
|
|
### Stripe Webhooks (local testing)
|
|
|
|
```bash
|
|
stripe listen --forward-to localhost:8080/api/webhooks/stripe
|
|
```
|
|
|
|
### Database reset
|
|
|
|
```bash
|
|
./gradlew reset # wipes DB volume and restarts containers
|
|
```
|
|
|
|
---
|
|
|
|
## Related Documents
|
|
|
|
- [REQUIREMENTS.md](./REQUIREMENTS.md) — Full product requirements and business model
|
|
- [CODING_GUIDELINES.md](./CODING_GUIDELINES.md) — Code conventions and standards
|