Live Application Link : https://ledger-blush-sigma.vercel.app/
Deployed Backend URL : https://ledger-mimp.onrender.com/
- Node.js + Express
- Supabase PostgreSQL
- JWT Authentication
- RBAC (viewer / analyst / admin)
- Validation + error handling
- Dashboard summary APIs
- React (Vite) dashboard with charts (Recharts)
Ledger/
backend/
src/
config/
controllers/
middleware/
models/
routes/
utils/
validation/
sql/
schema.sql
seed.sql
.env.example
package.json
frontend/
src/
public/
package.json
vite.config.js
docs/
API.md # All endpoints for Postman
ARCHITECTURE.md # Design, auth, data flow, middleware
- docs/API.md — full API list for API Testing (paths, methods, bodies)...I have tested APIs in postman
- docs/ARCHITECTURE.md — how auth, RBAC, rate limits, and soft delete fit together.
- docs/ENGINEERING_NOTES.md — scenario mapping, soft delete, trade-offs (good prep for interviews).
- docs/CODEBASE_MAP.md — every important file, what it does, and how data flows through it.
- docs/PRACTICES_AND_QUALITY.md — practices & concepts; maintainability, scalability, consistency.
- docs/DEPLOYMENT.md — deploy frontend on Vercel and backend on Render.
This setup part is explicitly for devs who want to clone the repo and experiment. For someone who wants to test can directly go through the given backend and frontend links.
- Go to backend:
cd backend - Install dependencies:
npm install
- Copy environment file:
On Windows PowerShell:
cp .env.example .env
Copy-Item .env.example .env - Updated
.envwith Supabase Postgres connection string.
- Create a Supabase project.
- Open SQL editor in Supabase (or connect via any PostgreSQL client).
- Run
backend/sql/schema.sql(new projects) or, if you already applied an older schema, runnpm run db:migrateonce to apply incremental SQL (record soft-delete, primary admin, user soft-delete columns, etc.). - Create your first admin user using
POST /api/auth/register(they become the primary admin : role cannot be removed and they cannot be deactivated). - Run
npm run db:seed(orbackend/sql/seed.sql). Sample rows usecreated_by= the user with the smallestid.
If you run seed with no users, the script exits with a hint to register first.
npm run devServer:
http://localhost:5000- Health check:
GET /api/health
From project root:
cd .\backend
npm install
Copy-Item .env.example .env
# edit .env with your Supabase DB URL
npm run devIn a second terminal, from project root:
cd .\frontend
npm install
npm run devOpen http://localhost:5173. The dev server proxies /api to http://localhost:5000, so keep the backend running.
- Register (first user only) → Sign in → dashboard with summary, trend chart, category bars, recent activity.
- Viewer sees summary + recent (own data); Analyst/Admin also see charts.
- Admin gets “Add transaction”, Team (user management with password confirmation for sensitive changes), and Records (search, filters, pagination, archive).
- Analyst/Viewer use Records with the same filters (viewer sees only their rows).
Production build: npm run build in frontend, then serve the frontend/dist folder with any static host (configure that host to proxy /api to your backend, or set VITE_API_BASE , see frontend/.env.example and frontend/src/api/client.js.
- First
POST /api/auth/registeruser is auto-created asadmin. - After first user:
registerrequires admin JWT token.
- Roles:
viewer: can only view own records, own summary, own recentanalyst: can view all records, all summaries, trends, category breakdownadmin: full records + users + category management
- Primary admin (first registered user): no other admin can demote them or deactivate them. Updating another user’s role or active status requires the acting admin’s password in the request body.
- JSON bodies are trimmed server-side (emails/passwords with accidental spaces).
- API routers are mounted from
backend/src/routes/index.js; each domain file exports its ownRouter.
POST /api/auth/registerPOST /api/auth/loginGET /api/auth/me(auth required)
GET /api/users?page=1&limit=10&search=(optional name/email search)GET /api/users/:idPOST /api/users— create user (name, email, password, role, optional is_active)PATCH /api/users/:id— body must includecurrentPassword(acting admin) plus any ofname,email,role,is_activeDELETE /api/users/:id— body:{ "currentPassword": "..." }(soft deactivate; cannot target primary admin or yourself)
GET /api/records?page=1&limit=10(viewer scoped to own, analyst/admin all) + filtersGET /api/records/:id(viewer own only, analyst/admin all)POST /api/records(admin)PATCH /api/records/:id(admin)DELETE /api/records/:id(admin) — soft delete (deleted = true); row hidden from lists and dashboard math
Supported filters in list:
type(income/expense)category(partial match, case-insensitive)search(matches notes or category, case-insensitive)from(YYYY-MM-DD)to(YYYY-MM-DD)pagelimit(default 10 on the server)
Pagination response includes:
rowspagelimittotaltotalPages
GET /api/dashboard/summaryGET /api/dashboard/recent?limit=10GET /api/dashboard/categories(analyst/admin only)GET /api/dashboard/trends?months=6(analyst/admin only)
GET /api/categories(all roles)POST /api/categories(admin only)DELETE /api/categories/:id(admin only)
POST /api/auth/register
{
"name": "Admin User",
"email": "admin@example.com",
"password": "Admin@1234"
}POST /api/auth/login
{
"email": "admin@example.com",
"password": "Admin@1234"
}POST /api/records
{
"amount": 5000,
"type": "income",
"category": "Salary",
"record_date": "2026-04-01",
"notes": "April salary"
}frontend/index.html is a simple local test UI.
Run quickly with VS Code Live Server or any static server.
It calls backend on http://localhost:5000/api.