A full-stack property listing platform for Nigeria: search and filter listings, view them on a map, request and
pay for inspections (Paystack), with separate dashboards for landlords/agents, property seekers, and admins.
“HomeHunt NG” and the logo are placeholders — change the name in components/Header.jsx, components/Footer.jsx,
app/layout.js (and the contact details in Admin → Settings).
Stack: Next.js 14 (App Router) · Tailwind CSS · SQLite via better-sqlite3 (real relational schema) ·
JWT cookie sessions + bcrypt · Leaflet/OpenStreetMap · Paystack.
npm install
cp .env.example .env.local # then set AUTH_SECRET (openssl rand -base64 48)
npm run seed # creates tables + DEMO data (safe to re-run)
npm run dev # http://localhost:3000
Production: npm run build && npm start (Node 18+; a C/C++ toolchain is needed the first time for better-sqlite3).
Password123!) — created by npm run seed| Role | Email | |—|—| | Admin | admin@demo.homehunt.ng | | Property owner | owner@demo.homehunt.ng | | Agent | agent@demo.homehunt.ng | | Seeker | seeker@demo.homehunt.ng |
Demo properties are flagged is_demo = 1, show a Demo badge, and are never marked verified.
Remove them with npm run seed:clear. Delete the demo accounts before going live.
.env.example)| Variable | Purpose |
|—|—|
| AUTH_SECRET | Required in production. Signs session tokens. |
| DATABASE_FILE | SQLite file path (use a persistent disk in production). |
| PAYSTACK_SECRET_KEY / PAYSTACK_PUBLIC_KEY | Live payments. Server-side only. With no valid key the app runs a demo checkout so the flow can be tested. |
| RESEND_API_KEY, EMAIL_FROM | Email provider (not yet wired — see below). |
| TERMII_API_KEY | SMS/OTP provider (not yet wired). |
| MAP_PROVIDER, NEXT_PUBLIC_MAPBOX_TOKEN | Map provider (OpenStreetMap works with no key). |
| NEXT_PUBLIC_APP_URL | Public URL (payment callbacks, sitemap, SEO). |
Paystack: set your keys in the environment, and in the Paystack dashboard set the webhook URL to
https://YOUR-DOMAIN/api/payments/webhook. Webhook signatures (HMAC-SHA512) are verified; confirmation is idempotent.
/properties/lagos) only for places that have listings, sitemap + robots.DELETE; admin actions are audit-logged.lib/db.js).Registration (validation, duplicates), login/logout, favourites persisting across logout/login, listing create → submit → admin approve → verify, edit-triggers-re-review, ownership protection (403), seeker blocked from admin/landlord APIs and pages, upload validation (bad type, disguised HTML, unauthenticated), rate limiting (429), search and natural-language query, inspection request → payment init → verify → replayed webhook (idempotent) → confirmed → reschedule notification, admin stats, reports, next build, production server responses, SEO metadata.
Not tested: live Paystack, real email/SMS, real-device mobile layouts (built responsive, not device-tested), load/performance.
lib/notify.js, app/api/auth/forgot-password, app/api/account/verify.app/api/admin/payments/[id].public/uploads). For multi-server hosting, move to S3/Cloudinary (app/api/upload/route.js). No server-side resizing (compression is in the browser).lib/db.js to Postgres.LIKE with aliases: fine for thousands of listings; add Meilisearch/Typesense at larger scale.is_featured exist), subscriptions, radius search, geocoding of typed addresses, admin per-listing detail page, saved-property alerts, document malware scanning, automated test suite.