Track your income and expenses with enterprise-grade architecture and security
- β¨ Modern UI: Beautiful, responsive design with Tailwind CSS and Radix UI components
- π Secure Authentication: JWT-based authentication with localStorage token storage (no CORS issues)
- π Transaction Management: Full CRUD operations for financial transactions
- π Visual Analytics: Interactive charts and comprehensive financial summaries
- ποΈ Type-Safe: Built entirely with TypeScript for better developer experience
- π Production Ready: Enterprise-grade deployment with nginx reverse proxy
- π± Mobile Responsive: Progressive Web App design for all device sizes
- π Cross-Origin Optimized: localStorage-based auth eliminates cookie CORS issues
- β‘ High Performance: nginx load balancing and optimized API endpoints
- π Security Focused: JWT tokens, password hashing, and secure headers
- π¦ CI/CD Pipeline: Automated testing and deployment workflows
βββββββββββββββββββ βββββββββββββββββββ βββββββββββββββββββ
β β β β β β
β React Frontendββββββ€ Express API ββββββ€ MongoDB β
β (Vite + TS) β β (Node.js) β β Database β
β localhost:3000β β localhost:5000 β β Port 27017 β
β β β β β β
βββββββββββββββββββ βββββββββββββββββββ βββββββββββββββββββ
Internet
β
βΌ
βββββββββββββββββββ
β AWS S3 Bucket β βββ Static Frontend Hosting
β React Build β (expencetracker9993)
β β
βββββββββββ¬ββββββββ
β HTTPS/HTTP Requests
βΌ
βββββββββββββββββββ
β AWS EC2 β βββ Backend Server (13.204.100.131)
β Ubuntu Server β
β β
β ββββββββββββββββ
β β nginx ββ βββ Reverse Proxy & Load Balancer
β β Port 80 ββ CORS Handler & Static Assets
β ββββββββ¬ββββββββ
β β β
β ββββββββΌββββββββ
β β Node.js ββ βββ Express API Server
β β Port 5000 ββ JWT Authentication
β β PM2 Managedββ Business Logic
β ββββββββ¬ββββββββ
βββββββββββΌββββββββ
β
βΌ
βββββββββββββββββββ
β MongoDB β βββ Database Server
β Atlas/Local β User Data & Transactions
β Port 27017 β Mongoose ODM
βββββββββββββββββββ
βββββββββββββββ βββββββββββββββ βββββββββββββββ βββββββββββββββ
β β β β β β β β
β S3 FrontendβββββΆβ nginx βββββΆβ Express API βββββΆβ MongoDB β
β β β (Port 80) β β(Port 5000) β β β
β β β β β β β β
ββββββββ¬βββββββ βββββββββββββββ ββββββββ¬βββββββ βββββββββββββββ
β β
β 1. Login Request β 2. JWT Token Response
β (email/password) β (stored in localStorage)
β β
β 3. Subsequent Requests β
β Authorization: Bearer <token> β
βββββββββββββββββββββββββββββββββββββββ
elegant-budget-tracker/
βββ π Backend/ # Node.js Express API
β βββ π src/
β β βββ π api/ # Business logic
β β β βββ user.ts # User authentication logic
β β β βββ transaction.ts # Transaction CRUD operations
β β βββ π database/ # Database configuration
β β β βββ db_connect.ts # MongoDB connection
β β β βββ π models/ # Mongoose schemas
β β β βββ user.ts # User model
β β β βββ transactions.ts # Transaction model
β β βββ π middleware/ # Express middleware
β β β βββ π jwt/ # JWT authentication
β β β βββ authenticateToken.ts # Token verification
β β βββ π routes/ # Route handlers (unused in current setup)
β β β βββ auth.ts # Authentication routes
β β β βββ transactions.ts # Transaction routes
β β βββ π types/ # TypeScript definitions
β β βββ express.d.ts # Express type extensions
β βββ server.ts # Main server file with all routes
β βββ ecosystem.config.js # PM2 process manager configuration
β βββ package.json # Dependencies and scripts
β βββ tsconfig.json # TypeScript configuration
β
βββ π Frontend/ # React Vite application
β βββ π src/
β β βββ π components/ # React components
β β β βββ π Dashboard/ # Dashboard-specific components
β β β β βββ BalanceSummary.tsx
β β β β βββ ExpensePieChart.tsx
β β β β βββ NewTransactionForm.tsx
β β β β βββ TransactionHistory.tsx
β β β βββ π ui/ # Reusable UI components (Radix)
β β β βββ ErrorBoundary.tsx
β β β βββ ProtectedRoute.tsx
β β βββ π context/ # React Context providers
β β β βββ AuthContext.tsx # localStorage-based auth context
β β β βββ ToastContext.tsx# Toast notifications
β β β βββ TransactionContext.tsx # Transaction state
β β βββ π hooks/ # Custom React hooks
β β β βββ use-logout.ts # Enhanced logout with localStorage
β β β βββ use-mobile.tsx # Mobile detection hook
β β β βββ use-toast.ts # Toast hook
β β βββ π lib/ # Utility libraries
β β β βββ axios.ts # Axios config with Bearer token
β β β βββ utils.ts # Utility functions
β β βββ π pages/ # Page components
β β β βββ Dashboard.tsx # Main dashboard
β β β βββ Index.tsx # Landing page
β β β βββ Login.tsx # Login page
β β β βββ SignUp.tsx # Registration page
β β β βββ NotFound.tsx # 404 page
β β βββ π types/ # TypeScript definitions
β β βββ index.ts # Type definitions
β βββ π public/
β β βββ _redirects # S3/Netlify routing configuration
β β βββ favicon.ico
β β βββ placeholder.svg
β β βββ robots.txt
β βββ vite.config.ts # Vite configuration
β βββ tailwind.config.ts # Tailwind CSS configuration
β βββ components.json # Shadcn/ui configuration
β βββ package.json # Dependencies and scripts
β
βββ nginx-expense-tracker.conf # nginx reverse proxy configuration
βββ README.md # This file
Make sure you have the following installed:
- Node.js (v18 or higher) - Download here
- MongoDB - Install locally or use MongoDB Atlas
- Git - Download here
- nginx (for production) - Install guide
git clone https://github.com/rohitTo95/elegant-budget-tracker.git
cd elegant-budget-tracker# Navigate to backend directory
cd Backend
# Install dependencies
npm install
# Create environment file (create .env based on your needs)
touch .env
# Add the following to .env file:
# MONGODB_URI=mongodb://localhost:27017/budget-tracker
# JWT_SECRET=your-super-secret-jwt-key-here
# PORT=5000
# NODE_ENV=development
# FRONTEND_URL=http://localhost:3000# Navigate to frontend directory (from project root)
cd Frontend
# Install dependencies
npm install
# Create environment file (create .env based on your needs)
touch .env
# Add the following to .env file:
# VITE_BACKEND_URL=http://localhost:5000# Terminal 1 - Start Backend
cd Backend
npm run dev:server
# Terminal 2 - Start Frontend
cd Frontend
npm run dev- Frontend: http://localhost:3000
- Backend API: http://localhost:5000
- API Root: http://localhost:5000/ (returns "API SERVER IS READY!")
# Database Configuration
MONGODB_URI=mongodb://localhost:27017/budget-tracker
# JWT Authentication
JWT_SECRET=your-super-secret-jwt-key-here
# Server Configuration
PORT=5000
NODE_ENV=development
# CORS Configuration
FRONTEND_URL=http://localhost:3000# Database Configuration
MONGODB_URI=mongodb://your-production-mongodb-uri
# JWT Authentication
JWT_SECRET=your-production-jwt-secret
# Server Configuration
PORT=5000
NODE_ENV=production
# CORS Configuration (AWS S3 Frontend)
FRONTEND_URL=http://expencetracker9993.s3-website.ap-south-1.amazonaws.com# API Configuration (Development)
VITE_BACKEND_URL=http://localhost:5000# API Configuration (Production via nginx)
VITE_BACKEND_URL=http://13.204.100.131- Development:
http://localhost:5000 - Production:
http://13.204.100.131
All API requests should include:
Content-Type: application/json
Protected endpoints require JWT token in header:
Authorization: Bearer <your-jwt-token>
GET /
Check if the API server is running.
curl -X GET http://localhost:5000/Response:
API SERVER IS READY!
POST /api/auth/signup
Register a new user account.
Request Body:
{
"name": "string (required)",
"email": "string (required, valid email)",
"password": "string (required)"
}Example:
curl -X POST http://localhost:5000/api/auth/signup \
-H "Content-Type: application/json" \
-d '{
"name": "John Doe",
"email": "john.doe@example.com",
"password": "SecurePassword123"
}'Success Response (201):
{
"message": "User created successfully",
"success": true
}Error Response (400):
{
"message": "User with this email already exists",
"error": "USER_EXISTS"
}POST /api/auth/login
Authenticate user and receive JWT token.
Request Body:
{
"email": "string (required)",
"password": "string (required)"
}Example:
curl -X POST http://localhost:5000/api/auth/login \
-H "Content-Type: application/json" \
-d '{
"email": "john.doe@example.com",
"password": "SecurePassword123"
}'Success Response (200):
{
"message": "Login successful",
"user": {
"id": "65f8b4c4d9e7f8a9b0c1d2e3",
"name": "John Doe",
"email": "john.doe@example.com",
"createdAt": "2024-01-15T10:30:00.000Z",
"updatedAt": "2024-01-15T10:30:00.000Z"
},
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}Error Response (401):
{
"message": "Invalid credentials",
"error": "INVALID_CREDENTIALS"
}GET /api/auth/check
Verify if the current JWT token is valid.
Headers Required:
Authorization: Bearer <your-jwt-token>
Example:
curl -X GET http://localhost:5000/api/auth/check \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."Success Response (200):
{
"success": true,
"user": {
"id": "65f8b4c4d9e7f8a9b0c1d2e3",
"name": "John Doe",
"email": "john.doe@example.com"
}
}Error Response (401):
{
"success": false,
"message": "Not authenticated"
}POST /api/auth/logout
Logout user (frontend should clear localStorage token).
Example:
curl -X POST http://localhost:5000/api/auth/logoutSuccess Response (200):
{
"success": true,
"message": "Logged out successfully"
}POST /api/transaction/create
Create a new income or expense transaction.
Headers Required:
Authorization: Bearer <your-jwt-token>
Content-Type: application/json
Request Body:
{
"type": "string (required: 'income' or 'expense')",
"amount": "number (required, positive)",
"category": "string (required)",
"description": "string (required)",
"date": "string (required, valid date format)"
}Example - Income Transaction:
curl -X POST http://localhost:5000/api/transaction/create \
-H "Content-Type: application/json" \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
-d '{
"type": "income",
"amount": 5000,
"category": "Salary",
"description": "Monthly salary payment",
"date": "2024-01-15"
}'Example - Expense Transaction:
curl -X POST http://localhost:5000/api/transaction/create \
-H "Content-Type: application/json" \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
-d '{
"type": "expense",
"amount": 1200,
"category": "Groceries",
"description": "Weekly grocery shopping",
"date": "2024-01-16"
}'Success Response (201):
{
"message": "Transaction created successfully",
"success": true
}Error Response (400) - Missing Fields:
{
"message": "All fields (userId, amount, description, category, type, date) are required",
"error": "MISSING_FIELDS"
}Error Response (400) - Invalid Amount:
{
"message": "Amount must be a valid number",
"error": "INVALID_AMOUNT"
}Error Response (400) - Invalid Type:
{
"message": "Type must be either \"income\" or \"expense\"",
"error": "INVALID_TYPE"
}Error Response (400) - Invalid Date:
{
"message": "Date must be in a valid format",
"error": "INVALID_DATE"
}GET /api/transactions
Retrieve all transactions for the authenticated user.
Headers Required:
Authorization: Bearer <your-jwt-token>
Example:
curl -X GET http://localhost:5000/api/transactions \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."Success Response (200):
{
"message": "Transactions retrieved successfully",
"success": true,
"transactions": [
{
"id": "65f8b5c4d9e7f8a9b0c1d2e4",
"type": "income",
"amount": 5000,
"category": "Salary",
"description": "Monthly salary payment",
"date": "2024-01-15T00:00:00.000Z",
"userId": "65f8b4c4d9e7f8a9b0c1d2e3",
"createdAt": "2024-01-15T10:35:00.000Z",
"updatedAt": "2024-01-15T10:35:00.000Z"
},
{
"id": "65f8b5c4d9e7f8a9b0c1d2e5",
"type": "expense",
"amount": 1200,
"category": "Groceries",
"description": "Weekly grocery shopping",
"date": "2024-01-16T00:00:00.000Z",
"userId": "65f8b4c4d9e7f8a9b0c1d2e3",
"createdAt": "2024-01-15T10:40:00.000Z",
"updatedAt": "2024-01-15T10:40:00.000Z"
}
]
}Error Response (401):
{
"message": "User authentication failed",
"error": "USER_NOT_AUTHENTICATED"
}PUT /api/transaction/:id
Update an existing transaction by ID.
Headers Required:
Authorization: Bearer <your-jwt-token>
Content-Type: application/json
Path Parameters:
id- Transaction ID (required)
Request Body (all fields optional):
{
"type": "string (optional: 'income' or 'expense')",
"amount": "number (optional)",
"category": "string (optional)",
"description": "string (optional)",
"date": "string (optional, valid date format)"
}Example:
curl -X PUT http://localhost:5000/api/transaction/65f8b5c4d9e7f8a9b0c1d2e4 \
-H "Content-Type: application/json" \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
-d '{
"amount": 5500,
"description": "Monthly salary payment - updated amount"
}'Success Response (200):
{
"message": "Transaction updated successfully",
"success": true,
"transaction": {
"id": "65f8b5c4d9e7f8a9b0c1d2e4",
"type": "income",
"amount": 5500,
"category": "Salary",
"description": "Monthly salary payment - updated amount",
"date": "2024-01-15T00:00:00.000Z",
"userId": "65f8b4c4d9e7f8a9b0c1d2e3",
"createdAt": "2024-01-15T10:35:00.000Z",
"updatedAt": "2024-01-15T11:00:00.000Z"
}
}Error Response (400) - Transaction Not Found:
{
"message": "Transaction not found or access denied",
"error": "TRANSACTION_NOT_FOUND"
}Error Response (401):
{
"message": "User authentication failed",
"error": "USER_NOT_AUTHENTICATED"
}DELETE /api/transaction/:id
Delete a transaction by ID.
Headers Required:
Authorization: Bearer <your-jwt-token>
Path Parameters:
id- Transaction ID (required)
Example:
curl -X DELETE http://localhost:5000/api/transaction/65f8b5c4d9e7f8a9b0c1d2e4 \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."Success Response (200):
{
"message": "Transaction deleted successfully",
"success": true
}Error Response (400) - Transaction Not Found:
{
"message": "Transaction not found or access denied",
"error": "TRANSACTION_NOT_FOUND"
}Error Response (401):
{
"message": "User authentication failed",
"error": "USER_NOT_AUTHENTICATED"
}We've provided a comprehensive testing script that tests all API endpoints with proper error handling and colored output. The script is located at test-api-endpoints.sh in the project root.
To run the automated tests:
# Make sure your backend server is running
cd Backend
npm run dev
# In another terminal, run the test script
cd /path/to/elegant-budget-tracker
chmod +x test-api-endpoints.sh
./test-api-endpoints.shThe script will:
- β Test all 9 API endpoints
- β Handle authentication flow automatically
- β Test error scenarios and edge cases
- β Provide colored output for easy reading
- β Generate detailed test results summary
- β Work without external dependencies (no jq required)
For manual testing or integration with other tools, here are individual curl commands:
BASE_URL="http://localhost:5000"
# 1. Test server status
curl -X GET $BASE_URL/
# 2. Register new user
curl -X POST $BASE_URL/api/auth/signup \
-H "Content-Type: application/json" \
-d '{
"name": "Test User",
"email": "test@example.com",
"password": "TestPassword123"
}'
# 3. Login and get token
curl -X POST $BASE_URL/api/auth/login \
-H "Content-Type: application/json" \
-d '{
"email": "test@example.com",
"password": "TestPassword123"
}'
# Extract token and use it in subsequent requests
TOKEN="your_jwt_token_here"
# 4. Check authentication
curl -X GET $BASE_URL/api/auth/check \
-H "Authorization: Bearer $TOKEN"
# 5. Create income transaction
curl -X POST $BASE_URL/api/transaction/create \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $TOKEN" \
-d '{
"type": "income",
"amount": 5000,
"category": "Salary",
"description": "Monthly salary",
"date": "2024-01-15"
}'
# 6. Get all transactions
curl -X GET $BASE_URL/api/transactions \
-H "Authorization: Bearer $TOKEN"
# 7. Test logout
curl -X POST $BASE_URL/api/auth/logout# Test missing authentication
curl -X GET http://localhost:5000/api/transactions
# Test invalid credentials
curl -X POST http://localhost:5000/api/auth/login \
-H "Content-Type: application/json" \
-d '{
"email": "wrong@example.com",
"password": "wrongpassword"
}'
# Test invalid transaction data
curl -X POST http://localhost:5000/api/transaction/create \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_TOKEN" \
-d '{
"type": "invalid_type",
"amount": "not_a_number",
"category": "",
"description": "",
"date": "invalid_date"
}'- Frontend: http://expencetracker9993.s3-website.ap-south-1.amazonaws.com
- Backend: http://13.204.100.131 (via nginx reverse proxy)
- API Base: http://13.204.100.131/api
# Test production API
curl -X GET http://13.204.100.131/
# Test CORS configuration
curl -H "Origin: http://expencetracker9993.s3-website.ap-south-1.amazonaws.com" \
-H "Access-Control-Request-Method: POST" \
-H "Access-Control-Request-Headers: Content-Type,Authorization" \
-X OPTIONS http://13.204.100.131/api/auth/login- Node.js - JavaScript runtime for server-side development
- Express.js - Minimal and flexible web application framework
- TypeScript - Type safety for backend development
- MongoDB - NoSQL document database
- Mongoose - MongoDB ODM with schema validation
- JWT (jsonwebtoken) - Stateless authentication tokens
- bcrypt - Password hashing for security
- CORS - Cross-origin resource sharing middleware
- React 18 - Modern UI library with hooks and context
- TypeScript - Type safety and better developer experience
- Vite - Fast build tool and development server
- Tailwind CSS - Utility-first CSS framework
- Radix UI - Accessible headless UI components
- React Hook Form - Performant form management
- Recharts - Responsive chart library for data visualization
- Axios - HTTP client with interceptors for JWT authentication
- React Router - Client-side routing with protected routes
- nginx - High-performance reverse proxy and load balancer
- PM2 - Advanced production process manager for Node.js
- AWS S3 - Static website hosting for frontend
- AWS EC2 - Virtual server for backend hosting
server {
listen 80;
server_name 13.204.100.131;
location /api {
proxy_pass http://localhost:5000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_cache_bypass $http_upgrade;
}
location / {
proxy_pass http://localhost:5000;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}# Start the application
cd Backend
pm2 start ecosystem.config.js --env production
# Monitor the application
pm2 logs
pm2 status| Status Code | Error | Description | Solution |
|---|---|---|---|
| 400 | MISSING_FIELDS | Required fields not provided | Check request body contains all required fields |
| 400 | INVALID_AMOUNT | Amount is not a valid number | Ensure amount is a positive number |
| 400 | INVALID_TYPE | Transaction type invalid | Use only 'income' or 'expense' |
| 400 | INVALID_DATE | Date format invalid | Use valid date format (YYYY-MM-DD) |
| 401 | USER_NOT_AUTHENTICATED | Missing or invalid JWT token | Include valid Authorization header |
| 401 | INVALID_CREDENTIALS | Wrong email/password | Check login credentials |
| 404 | TRANSACTION_NOT_FOUND | Transaction doesn't exist | Verify transaction ID and ownership |
| 500 | SERVER_ERROR | Internal server error | Check server logs and database connection |
# Check backend logs
pm2 logs
# Test database connection
mongo mongodb://localhost:27017/budget-tracker
# Check if backend is running
curl -X GET http://localhost:5000/
# Verify JWT token (decode)
# Use jwt.io to decode and verify token structureThis project is licensed under the MIT License.
Rohit - GitHub Profile
β Star this repository if you find it helpful!
π Deployed on AWS with enterprise-grade architecture
Made with β€οΈ by Rohit