A modern web app to digitize loan management — track clients, record payments, and monitor portfolio health in one place.
Loan Tracker replaces paper-based workflows with a lightweight, multilingual system built for loan managers and administrators. Managers handle day-to-day lending operations; administrators oversee users, audit activity, and view organization-wide metrics.
- ✨ Features
- 🛠 Tech stack
- 📋 Prerequisites
- 🚀 Getting started
- 👑 First administrator
- 🔐 Environment variables
- 📜 Scripts
- 📁 Project structure
- 📖 API documentation
- 🌍 Internationalization
- ☁️ Deployment
- 🛡 Security
- 🧪 Testing
- 📄 License
- 👤 Author
|
|
| Requirement | |
|---|---|
| 🟢 | Node.js 20+ |
| 🐘 | PostgreSQL 14+ (Neon, Supabase, or local) |
| 📦 | npm 9+ |
git clone https://github.com/msaouab/loantracker.git
cd loantracker
npm installCreate a .env file in the project root:
# 🗄 Database (required)
DATABASE_URL="postgresql://user:password@host:5432/loantracker?sslmode=require"
# 🔐 Auth (required)
AUTH_SECRET="generate-with-openssl-rand-base64-32"
NEXTAUTH_URL="http://localhost:3000"
# 🏷 App (optional)
NEXT_PUBLIC_APP_NAME="Loan Tracker"
NEXT_PUBLIC_APP_URL="http://localhost:3000"
# 🌱 Seed — demo manager (required for db:seed)
SEED_USER_EMAIL="manager@example.com"
SEED_USER_PASSWORD="change-me-in-production"
SEED_USER_NAME="Demo Manager"
# ⏰ Production cron (required on Vercel)
CRON_SECRET="generate-a-long-random-secret"Generate AUTH_SECRET:
openssl rand -base64 32
⚠️ Never commit.env— it contains secrets.
npx prisma migrate deploynpm run db:seedCreates a demo manager (not an admin) with sample loans, payments, and activity logs.
npm run devOpen http://localhost:3000 🎉
The seed script does not create an admin account.
Option A — SQL (recommended for fresh installs)
UPDATE users
SET "isAdmin" = true, "isActivated" = true
WHERE email = 'your-admin@example.com';Option B — Admin UI
Once an admin exists: Admin → Users → Create user → enable Administrator.
| Variable | Description | |
|---|---|---|
DATABASE_URL |
✅ | PostgreSQL connection string (pooled) |
DIRECT_URL |
⬜ | Direct URL for Prisma migrations |
AUTH_SECRET |
✅ | Session signing secret |
NEXTAUTH_URL |
✅ | Canonical app URL |
NEXT_PUBLIC_APP_NAME |
⬜ | UI display name |
NEXT_PUBLIC_APP_URL |
⬜ | Public app URL |
SEED_USER_EMAIL |
🌱 | Demo manager email |
SEED_USER_PASSWORD |
🌱 | Demo manager password |
SEED_USER_NAME |
⬜ | Demo manager name |
CRON_SECRET |
🚀 | Bearer token for purge cron |
✅ Required · ⬜ Optional · 🌱 Seed only · 🚀 Production
| Command | Description | |
|---|---|---|
npm run dev |
🖥 | Start development server |
npm run build |
📦 | Migrate, generate client & build |
npm run start |
🚀 | Start production server |
npm run lint |
🔍 | Run ESLint |
npm test |
🧪 | Run Vitest unit tests |
npm run db:seed |
🌱 | Seed demo data |
loantracker/
├── 📂 app/ # Next.js App Router (pages, layouts, API)
│ ├── (app)/ # Authenticated manager & admin pages
│ ├── auth/ # Login and registration
│ └── api/ # REST API endpoints
├── 🧩 components/ # React components (UI, forms, dashboards)
├── ⚙️ lib/ # Business logic, services, validation
├── 🌍 messages/ # i18n translations (en, fr, ar)
├── 🗄 prisma/ # Schema, migrations, seed
├── 🔗 i18n/ # next-intl config
└── 🖼 public/ # Static assets
| Endpoint | Access | Description |
|---|---|---|
/api/openapi |
Admin | OpenAPI specification |
/docs |
Admin | Interactive Swagger UI |
API areas: Auth · Loans · Payments · Settings · Admin · Cron
POST /api/cron/purge-loans
Authorization: Bearer <CRON_SECRET>| Code | Language | Direction |
|---|---|---|
en |
🇬🇧 English | LTR |
fr |
🇫🇷 French | LTR |
ar |
🇲🇦 Arabic | RTL |
Locale is persisted per user. Currency is formatted in Moroccan Dirham (DH).
- 📤 Push to GitHub and import in Vercel
- 🐘 Add PostgreSQL (Neon via Marketplace)
- 🔐 Set environment variables
- 🚀 Deploy —
npm run buildruns migrations automatically
vercel.json runs daily at 03:00 UTC:
| Retention | |
|---|---|
| 🗑 Soft-deleted loans | 30 days |
| ✅ Recovered loans | 30 days |
| 📋 Old activity logs | Pruned |
Set CRON_SECRET in Vercel — cron sends Authorization: Bearer $CRON_SECRET automatically.
| 🔒 | Passwords hashed with bcrypt |
| 🍪 | HTTP-only session cookies (__Host- in production) |
| 🛂 | Server-side auth & role checks on all API routes |
| 🌐 | Anonymized IPs in activity logs (/24 truncation) |
| 📊 | No PII in analytics providers |
npm testCovers validations, dashboard calculations, activity queries, and API helpers.
Private project. All rights reserved.