Ledgera is a full-stack collaborative finance platform built for modern teams and personal workflows. It combines workspace-based financial management, shared records, analytics dashboards, and role-based collaboration into a streamlined premium SaaS experience.
- JWT-based authentication and authorization
- OTP-based password reset flow with email integration (Resend API)
- Rate limiting for password reset requests (3 per 15 minutes)
- Multi-level role-based access control (Admin/Analyst/Viewer)
- Secure token management with configurable expiry
- Protected routes and API endpoints
- Multi-workspace support for team collaboration
- Workspace-scoped financial records and analytics
- Three permission levels: Owner, Editor, Viewer
- Workspace member management and invitations
- Automatic workspace switching
- Workspace deletion with safety validations
- Income and expense tracking with custom categories
- Advanced filtering and search capabilities
- Workspace-scoped transaction management
- Permission-based record creation/editing
- Transaction history with user attribution
- Real-time data synchronization
- Interactive dashboard with real-time analytics
- Monthly trend charts (income vs expenses)
- Category-wise spending breakdown
- Income vs expense comparisons
- Recent activity feed
- Workspace-specific analytics
- Dedicated admin panel for platform-wide user management
- User activation/deactivation controls
- Search and filter users by status, role, email
- View user workspace associations
- Prevent self-deactivation safeguards
- Professional admin UX with confirmation modals
- System theme detection (Light/Dark/System)
- Responsive design for mobile and desktop
- Modern glassmorphic UI components
- Smooth animations and transitions
- Toast notifications for user feedback
- Accessible components (WCAG considerations)
- RESTful API with Spring Boot
- Layered architecture (Controller → Service → Repository)
- Database migrations with Flyway
- Comprehensive error handling with user-friendly messages
- Detailed logging and monitoring
- Workspace context management
Ledgera demonstrates production-grade full-stack development practices:
- Enterprise Architecture — Layered backend design with clear separation of concerns
- Multi-Tenancy — Workspace-based architecture for team collaboration
- Security First — JWT authentication, role-based access control, workspace permissions, rate limiting
- Modern Stack — Spring Boot 3, React 18, TypeScript, PostgreSQL
- Email Integration — Professional OTP-based password reset flow with Resend API
- Scalable Design — RESTful API, database migrations, comprehensive error handling
- Admin Platform — Dedicated admin panel for platform-wide user management
- Modern UX — System theme detection, responsive design, accessible components
- Developer Experience — Hot reload, TypeScript, ESLint, detailed logging
Built to reflect production-level design practices used in modern SaaS applications.
- Language: Java 17+
- Framework: Spring Boot 3.2.x
- Security: Spring Security with JWT
- Database: Spring Data JPA, Flyway migrations
- Email: Resend API (v3.0.0)
- Rate Limiting: Bucket4j (v8.7.0)
- Build Tool: Maven
- Framework: React 18 with TypeScript
- Build Tool: Vite 5
- Routing: React Router v6
- HTTP Client: Axios
- Charts: Recharts
- UI Components: Radix UI, Tailwind CSS
- State Management: Zustand
- Forms: React Hook Form with Zod validation
- Notifications: Sonner
- Production: PostgreSQL (Neon serverless)
- Development: H2 (in-memory, optional profile)
- Backend Hosting: Render (Docker)
- Frontend Hosting: Vercel
- Email Service: Resend
- Version Control: Git
ledgera/
├─ backend/ # Spring Boot API
│ ├─ src/main/java/com/ledgera/
│ │ ├─ config/ # Configuration classes
│ │ │ ├─ DataInitializer.java # Seed data
│ │ │ ├─ EmailConfig.java # Resend email client
│ │ │ ├─ RateLimitConfig.java # Rate limiting
│ │ │ └─ SecurityConfig.java # Spring Security
│ │ ├─ controller/ # REST controllers
│ │ │ ├─ AdminUserController.java
│ │ │ ├─ AuthController.java
│ │ │ ├─ DashboardController.java
│ │ │ ├─ FinancialRecordController.java
│ │ │ ├─ HealthController.java
│ │ │ ├─ OtpController.java
│ │ │ ├─ UserController.java
│ │ │ ├─ WorkspaceController.java
│ │ │ └─ WorkspaceMemberController.java
│ │ ├─ dto/ # Data Transfer Objects
│ │ ├─ entity/ # JPA entities
│ │ │ ├─ FinancialRecord.java
│ │ │ ├─ User.java
│ │ │ ├─ Workspace.java
│ │ │ ├─ WorkspaceInvitation.java
│ │ │ └─ WorkspaceMember.java
│ │ ├─ enums/ # Enumerations
│ │ │ ├─ Role.java
│ │ │ ├─ TransactionType.java
│ │ │ └─ WorkspacePermission.java
│ │ ├─ exception/ # Exception handling
│ │ │ └─ GlobalExceptionHandler.java
│ │ ├─ repository/ # Data access layer
│ │ │ ├─ FinancialRecordRepository.java
│ │ │ ├─ FinancialRecordSpecification.java
│ │ │ ├─ UserRepository.java
│ │ │ ├─ WorkspaceRepository.java
│ │ │ ├─ WorkspaceInvitationRepository.java
│ │ │ └─ WorkspaceMemberRepository.java
│ │ ├─ security/ # Security components
│ │ │ ├─ CustomUserDetailsService.java
│ │ │ ├─ JwtAuthenticationFilter.java
│ │ │ ├─ JwtTokenProvider.java
│ │ │ ├─ RequireWorkspacePermission.java
│ │ │ ├─ WorkspaceContextHolder.java
│ │ │ └─ WorkspacePermissionEvaluator.java
│ │ └─ service/ # Business logic
│ │ ├─ AdminUserService.java
│ │ ├─ AuthService.java
│ │ ├─ CurrentUserService.java
│ │ ├─ DashboardService.java
│ │ ├─ EmailService.java
│ │ ├─ FinancialRecordService.java
│ │ ├─ UserService.java
│ │ ├─ WorkspaceService.java
│ │ └─ WorkspaceMemberService.java
│ ├─ src/main/resources/
│ │ ├─ application.properties # Main config
│ │ ├─ application-h2.properties # H2 profile
│ │ └─ db/migration/ # Flyway migrations
│ │ ├─ V1__init.sql
│ │ ├─ V2__backfill_financial_record_users.sql
│ │ ├─ V3__add_workspaces.sql
│ │ ├─ V4__add_otp_fields.sql
│ │ ├─ V5__update_workspace_names_to_first_name.sql
│ │ └─ V6__ensure_workspace_owners_are_members.sql
│ ├─ .env # Environment variables
│ ├─ .env.example # Environment template
│ ├─ Dockerfile # Docker configuration
│ ├─ docker-compose.yml # Docker Compose
│ └─ pom.xml # Maven dependencies
│
├─ frontend/ # React + Vite SPA
│ ├─ src/
│ │ ├─ api/ # API client
│ │ │ ├─ adminApi.ts
│ │ │ ├─ authApi.ts
│ │ │ ├─ client.ts
│ │ │ ├─ dashboardApi.ts
│ │ │ ├─ recordsApi.ts
│ │ │ ├─ usersApi.ts
│ │ │ ├─ workspaceApi.ts
│ │ │ └─ workspaceMemberApi.ts
│ │ ├─ components/ # React components
│ │ │ ├─ auth/ # Auth components
│ │ │ ├─ backend/ # Backend status
│ │ │ ├─ dashboard/ # Dashboard widgets
│ │ │ ├─ landing/ # Landing page
│ │ │ ├─ layout/ # Layout components
│ │ │ ├─ records/ # Record components
│ │ │ ├─ workspace/ # Workspace components
│ │ │ └─ ui/ # UI primitives
│ │ ├─ config/ # Configuration
│ │ │ └─ brandAssets.ts # Logo & branding
│ │ ├─ contexts/ # React contexts
│ │ │ ├─ AuthContext.tsx
│ │ │ ├─ SidebarContext.tsx
│ │ │ ├─ ThemeContext.tsx
│ │ │ └─ WorkspaceContext.tsx
│ │ ├─ hooks/ # Custom hooks
│ │ ├─ pages/ # Page components
│ │ │ ├─ admin/ # Admin pages
│ │ │ ├─ auth/ # Auth pages
│ │ │ ├─ dashboard/ # Dashboard page
│ │ │ ├─ records/ # Records page
│ │ │ ├─ workspace/ # Workspace pages
│ │ │ └─ LandingPage.tsx
│ │ ├─ store/ # State management
│ │ ├─ types/ # TypeScript types
│ │ ├─ utils/ # Utility functions
│ │ ├─ App.tsx # Root component
│ │ └─ main.tsx # Entry point
│ ├─ public/
│ │ ├─ icon.svg # App logo (SVG)
│ │ ├─ icon.png # App logo (PNG)
│ │ └─ site.webmanifest # PWA manifest
│ ├─ .env # Environment variables
│ ├─ .env.example # Environment template
│ ├─ index.html # HTML template
│ ├─ package.json # Dependencies
│ ├─ tsconfig.json # TypeScript config
│ ├─ vite.config.ts # Vite config
│ └─ vercel.json # Vercel config
│
├─ public/ # Shared assets
│ ├─ icon.svg # Ledgera logo
│ ├─ icon.png # Ledgera logo (PNG)
│ └─ site.webmanifest # PWA manifest
│
├─ .gitignore # Git ignore rules
└─ README.md # This file
Create a backend/.env file with the following variables:
# Database Configuration
DB_URL=jdbc:postgresql://your-db-host/your-database?sslmode=require
DB_USERNAME=your_username
DB_PASSWORD=your_password
# JWT Configuration
JWT_SECRET=your-secret-key-at-least-256-bits-long
JWT_EXPIRATION=86400000
# Resend Email Configuration
RESEND_API_KEY=your_resend_api_key
RESEND_FROM_EMAIL=your-verified-email@yourdomain.com
RESEND_FROM_NAME=Ledgera
# Application Configuration
APP_BASE_URL=http://localhost:5173Note: Use backend/.env.example as a reference template.
The frontend uses a centralized API client for all backend requests. See frontend/API_CONFIGURATION.md for detailed documentation.
Development (.env.local):
# Uses Vite proxy to avoid CORS issues
VITE_API_BASE_URL=/apiProduction (.env and .env.production):
# Direct backend URL
VITE_API_BASE_URL=https://ledgera-backend.onrender.com/apiKey Features:
- ✅ Centralized Axios client with automatic authentication
- ✅ Consistent error handling across all API calls
- ✅ Environment-based configuration (dev/prod)
- ✅ No hardcoded URLs or direct fetch calls
- ✅ 30-second timeout for all requests
- ✅ Automatic 401 handling with redirect to login
For production deployments:
- Set
VITE_API_BASE_URLto your backend URL in your hosting platform - All API calls automatically use this centralized configuration
- Java 17+ (JDK)
- Node.js 18+ (frontend includes
.nvmrcwith18) - npm or yarn
- PostgreSQL (or use H2 for testing)
- Maven (included via Maven Wrapper)
git clone https://github.com/yourusername/ledgera.git
cd ledgeracd backend
# Copy environment template
cp .env.example .env
# Edit .env with your configuration
# Add database credentials, JWT secret, Resend API key, etc.
# Run the application
./mvnw spring-boot:runBackend runs on: http://localhost:8080
Optional: Use H2 in-memory database for testing:
./mvnw spring-boot:run -Dspring-boot.run.profiles=h2cd frontend
# Install dependencies
npm install
# Copy environment template (if needed)
cp .env.example .env
# Start development server
npm run devFrontend runs on: http://localhost:5173
- Frontend: http://localhost:5173
- Backend API: http://localhost:8080
- Health Check: http://localhost:8080/healthz
If data initialization is enabled:
- Email: rakinmohammedrafeeq@gmail.com
- Password: admin123
Note: On first login, a default workspace is automatically created for each user.
cd backend
./mvnw clean package
# Run the JAR
java -jar target/ledgera-*.jarcd frontend
npm run build
# Preview production build
npm run preview./mvnw spring-boot:run— Start development server./mvnw clean package— Build production JAR./mvnw test— Run tests./mvnw clean— Clean build artifacts
npm run dev— Start Vite dev server (http://localhost:5173)npm run build— Production buildnpm run preview— Preview production buildnpm run lint— Run ESLintnpm run type-check— TypeScript type checking
POST /api/auth/register— User registrationPOST /api/auth/login— User login (returns JWT)POST /api/auth/request-otp— Request OTP for password resetPOST /api/auth/verify-otp— Verify OTP codePOST /api/auth/reset-password— Reset password with OTP
GET /api/users/me— Get current user profilePUT /api/users/me— Update current user profile
GET /api/admin/users— List all users with pagination (Admin only)PUT /api/admin/users/{id}/status— Activate/deactivate user (Admin only)
GET /api/workspaces— List user's workspacesPOST /api/workspaces— Create new workspaceGET /api/workspaces/{id}— Get workspace detailsPUT /api/workspaces/{id}— Update workspace (Owner only)DELETE /api/workspaces/{id}— Delete workspace (Owner only)POST /api/workspaces/{id}/switch— Switch to workspace
GET /api/workspaces/{workspaceId}/members— List workspace membersPOST /api/workspaces/{workspaceId}/members/invite— Invite member (Owner only)PUT /api/workspaces/{workspaceId}/members/{memberId}— Update member permission (Owner only)DELETE /api/workspaces/{workspaceId}/members/{memberId}— Remove member (Owner only)
GET /api/records— List records with filtering (workspace-scoped)POST /api/records— Create new record (Editor/Owner only)GET /api/records/{id}— Get record by IDPUT /api/records/{id}— Update record (Editor/Owner only)DELETE /api/records/{id}— Delete record (Editor/Owner only)
GET /api/dashboard— Get dashboard analytics (workspace-scoped)
GET /healthz— Health check endpoint (unauthenticated)
- Create a new Web Service on Render
- Connect your repository
- Configure build settings:
- Build Command:
cd backend && ./mvnw clean package - Start Command:
java -jar backend/target/*.jar
- Build Command:
- Set environment variables:
DB_URL,DB_USERNAME,DB_PASSWORDJWT_SECRET,JWT_EXPIRATIONRESEND_API_KEY,RESEND_FROM_EMAIL,RESEND_FROM_NAMEAPP_BASE_URL(your frontend URL)
- Health check:
/healthz
- Import your repository to Vercel
- Configure build settings:
- Framework Preset: Vite
- Root Directory:
frontend - Build Command:
npm run build - Output Directory:
dist
- Set environment variables:
VITE_API_BASE_URL(your backend URL, e.g.,https://your-backend.onrender.com/api)
- Deploy
- Sign up at resend.com
- Verify your domain or use
onboarding@resend.devfor testing - Generate API key and add to backend environment variables
- Configure email templates in
EmailService.java
- Create a PostgreSQL database on Neon
- Copy connection string to
DB_URL - Flyway migrations run automatically on startup
Interactive showcase with animated statistics and smooth transitions.
Real-time analytics with monthly trends, category breakdowns, and recent activity. Workspace-scoped data visualization.
Advanced filtering, search, and management of transactions. Permission-based access controls.
Create and manage multiple workspaces. Invite team members with granular permissions (Owner/Editor/Viewer).
Platform-wide user management with activation controls, search, and filtering capabilities.
Secure login, registration, and OTP-based password reset flow.
Automatic system theme detection with manual Light/Dark/System mode selection.
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/AmazingFeature) - Commit your changes (
git commit -m 'Add some AmazingFeature') - Push to the branch (
git push origin feature/AmazingFeature) - Open a Pull Request
This project is licensed under the MIT License.
For questions, suggestions, or collaboration:
- Email: rakinmohammedrafeeq@gmail.com
- LinkedIn: linkedin.com/in/rakinmohammedrafeeq
- GitHub: github.com/rakinmohammedrafeeq
If you find this project useful:
- ⭐ Star the repository on GitHub
- Report issues or suggest features
- Contribute via pull requests
- ☕ Support my work:
Built with ❤️ by Rakin Mohammed Rafeeq