A full-stack real estate platform built with Spring Boot 3.3, Thymeleaf + Bootstrap 5, PostgreSQL, and Spring Security. Implements all 12 screens from the provided UI design, with full listing CRUD, role-based access control, two-factor authentication, transactional email notifications, image uploads, and a searchable audit log.
| Layer | Technology |
|---|---|
| Backend | Spring Boot 3.3 (Java 17) |
| Frontend | Thymeleaf + Bootstrap 5 + Bootstrap Icons + Chart.js |
| Database | PostgreSQL 14+ |
| ORM | Spring Data JPA / Hibernate |
| Auth | Spring Security (BCrypt, form login, 2FA, role-based, single-session) |
| Testing | JUnit 5 + Mockito + Spring Boot Test + H2 in-memory |
| Build | Maven |
| Storage | Cloudinary (production) / Local filesystem (development fallback) |
| Deployment | Docker + Railway |
| Route | Screen |
|---|---|
/ |
Landing page — hero, search, features grid, stats band |
/register |
Registration — password rules, GDPR consent, notification opt-ins |
/login |
Login — single-session policy, 2FA flow |
/listings |
Browse Listings — filters (location, price, type, bedrooms), sort, pagination |
/listings/{id} |
Property Detail — gallery, agent card, inquiry & viewing modals |
/saved |
Saved Properties — watchlist + clear all |
/inquiries |
Inquiries & Viewings — tabbed view, KPI summary |
/payment/{id} |
Premium Booking Payment — card / bank transfer, order summary + emailed receipt |
/account/notifications |
Notification Preferences — GDPR-compliant toggles |
/admin |
Admin Dashboard — KPIs, listing moderation (ADMIN) |
/reports |
Supervisor Reports — Chart.js charts, top performers (SUPERVISOR / ADMIN) |
/account |
Account & Privacy — profile, password, 2FA toggle, GDPR controls |
| Endpoint | Action |
|---|---|
GET /admin/listings/new |
Create form (ADMIN and AGENT) |
POST /admin/listings |
Persist — AGENT creations land in PENDING_REVIEW; ADMIN auto-publishes |
GET /admin/listings/{id}/edit |
Edit form with upload area and image grid |
POST /admin/listings/{id} |
Save changes |
POST /admin/listings/{id}/images |
Multipart image upload → Cloudinary or local /uploads |
POST /admin/listings/{propertyId}/images/{imageId}/delete |
Remove image + delete blob |
POST /admin/listings/{id}/delete |
Delete listing |
POST /admin/listings/{id}/approve / reject |
Moderation (ADMIN only) |
Cloudinary (production — backend-direct upload, secret stays server-side):
export CLOUDINARY_CLOUD_NAME=...
export CLOUDINARY_API_KEY=...
export CLOUDINARY_API_SECRET=...Local filesystem (development fallback — works out-of-the-box, no credentials needed):
export LOCAL_UPLOAD_DIR=/var/homefinder/uploads # optional overrideFull searchable audit trail across authentication, user management, listing lifecycle, inquiries, payments, and image operations.
- Filters: actor email (contains), action type, date range
- Paginated table (25 entries/page)
- Retention: 3 months
Events captured:
| Category | Events |
|---|---|
| Auth | LOGIN_SUCCESS, LOGIN_FAILURE |
| Users | USER_REGISTER, PASSWORD_CHANGE, TWO_FA_TOGGLE |
| Listings | LISTING_CREATE, LISTING_UPDATE, LISTING_DELETE, LISTING_APPROVE, LISTING_REJECT |
| Interactions | INQUIRY_CREATE, VIEWING_SCHEDULE, PAYMENT_PROCESS |
| Images | IMAGE_UPLOAD, IMAGE_DELETE |
Each entry records: actor email, action, target type + id, details, IP address, timestamp.
EmailService sends HTML emails asynchronously (@Async / @EnableAsync):
- Inquiry confirmation → user
- Viewing confirmation → user
- Payment receipt → user
- New listing notification → subscribed users (Observer pattern)
Disabled by default — logs instead of sending. Enable with:
export MAIL_ENABLED=true
export MAIL_HOST=smtp.gmail.com
export MAIL_PORT=587
export MAIL_USERNAME=...
export MAIL_PASSWORD=...
export MAIL_FROM=no-reply@yourdomain.com- BCrypt password hashing
- Role-based authorization:
USER,AGENT,ADMIN,SUPERVISOR - Mandatory two-factor authentication (2FA) via 6-digit email token
- Single active session — new login invalidates previous session
- CSRF enabled (Thymeleaf forms get tokens automatically)
@EnableMethodSecurity—@PreAuthorizeon sensitive controller methods
- JDK 17+
- Maven 3.9+
- PostgreSQL 14+
CREATE USER homefinder WITH PASSWORD 'homefinder';
CREATE DATABASE homefinder OWNER homefinder;
GRANT ALL PRIVILEGES ON DATABASE homefinder TO homefinder;Override defaults with environment variables if needed:
export DB_URL=jdbc:postgresql://localhost:5432/homefinder
export DB_USER=homefinder
export DB_PASSWORD=homefindermvn spring-boot:runApp runs at http://localhost:8080.
The DataSeeder runs in two passes on every boot:
- Admin bootstrap — runs every restart, never duplicates. 2FA is disabled on this account so demo logins don't require an SMTP-delivered token.
- Demo content — runs only on a fresh install (zero properties in DB). Seeds supervisor / agent / user accounts, 2 agents, 7 properties (mixed sale/rent, one premium), 1 inquiry, and 1 viewing.
| Password | Role | 2FA | Notes | |
|---|---|---|---|---|
admin@homefinder.com |
Admin123! |
ADMIN | off | bootstrapped on every start |
supervisor@homefinder.com |
Super@123 |
SUPERVISOR | on | demo seed only |
agent@homefinder.com |
Agent@123 |
AGENT | on | demo seed only |
jane.doe@example.com |
Demo@1234 |
USER | on | demo seed only |
Override bootstrap admin without touching code:
export HOMEFINDER_ADMIN_EMAIL=you@example.com
export HOMEFINDER_ADMIN_PASSWORD='your-strong-password'After login, users are redirected based on role:
| Role | Lands on |
|---|---|
| ADMIN | /admin |
| SUPERVISOR | /reports |
| AGENT | /listings |
| USER | /listings |
Disable all seeding with homefinder.seed.enabled=false.
mvn testTests run against an in-memory H2 using src/test/resources/application-test.properties.
| Area | Class |
|---|---|
| Registration validation | RegisterFormValidationTest |
| User registration service | UserServiceTest |
| 2FA token issue / verify / expiry | TwoFactorServiceTest |
| Browse filters → repository | PropertyServiceTest |
| Bedroom range parsing | ListingBedroomFilterTest |
| Inquiries / viewings | InquiryServiceTest |
| Supervisor KPI model | ReportServiceTest |
| Local storage upload / delete | LocalStorageServiceTest |
| Area | Class |
|---|---|
| JPA + public property search | PropertyAndInquiryRepositoryIT |
| Spring context sanity check | HomeFinderApplicationTests |
| Security / role enforcement / CSRF | SecurityIntegrationTest |
Surefire reports: target/surefire-reports/ after mvn test.
mvn clean package
java -jar target/homefinder.jarhomefinder/
├── pom.xml
├── README.md
├── TESTING.md
└── src/
├── main/java/com/homefinder/
│ ├── HomeFinderApplication.java
│ ├── config/ (SecurityConfig, StorageConfig, AsyncConfig, DataSeeder)
│ ├── controller/ (HomeController, AuthController, ListingController, ...)
│ ├── dto/ (RegisterForm, ListingForm, PropertySearchCriteria, ...)
│ ├── entity/ (User, Property, Inquiry, Viewing, Payment, AuditLog, ...)
│ ├── repository/ (Spring Data JPA interfaces)
│ ├── security/ (CustomUserDetailsService, TwoFactorAuthenticationSuccessHandler)
│ └── service/ (UserService, PropertyService, TwoFactorService, AuditService, ...)
│ ├── observer/ (PropertyObserver, PropertySubject, NewListingEmailObserver)
│ └── storage/ (StorageService, CloudinaryStorageService, LocalFilesystemStorageService)
├── main/resources/
│ ├── application.properties
│ ├── static/ (css/, js/, img/)
│ └── templates/ (12 Thymeleaf views)
└── test/java/com/homefinder/
| Pattern | Implementation | Purpose |
|---|---|---|
| Observer | PropertySubject → PropertyObserver → NewListingEmailObserver |
Automated email notifications on new listings (RE-10) |
| Strategy | StorageService → CloudinaryStorageService / LocalFilesystemStorageService |
Pluggable image storage selected at runtime |
| Singleton | AuditService (@Service Spring bean) |
Centralised audit logging with 3-month retention (RE-14) |