FFA is a full-stack application managing incomes and expenses built specifically for freelancer delivery drivers. Built with Angular for the frontend, Spring Boot for the backend, and MongoDB as the database, this project helps users track their finances efficiently.Its architecture ensures maintainability, scalability, and extensibility, setting a solid foundation for future enhancements. This is a minimum viable product with full functionality.
TODO:
- Add tax brackets and automatic gross income calculation.
- Add Help tool tips.
- Make a more modern design.
- Android integration.
- Launch on Google Play.
The App features:
Clear separation of concerns between frontend, backend, and database.
Angular Material Integration
Standalone Components
Real-Time Pagination
Charting Integration
Dynamic Forms
Notifications and Feedback
Spring Boot Framework:
- Rapid development with built-in features like dependency injection, data binding, and robust error handling.
Layered Architecture:
-
Controllers, services, and DAOs (repositories) are separated for clear responsibilities and maintainability.
-
Code Reusability (DTO pattern)
RESTful APIs
Backend Pagination
Custom Jwt Middleware
Spring Security
JWT-based authentication.
Password Hashing
Data Validation
Swagger/OpenAPI Integration
To build and deploy the application, ensure the following tools are installed on your system:
Java Development Kit (JDK) 17 or newer
Gradle 8.x (or use the Gradle wrapper)
MongoDB (local or remote instance)
Node.js 18.x or newer
Angular CLI 15.x or newer
Follow these steps to clone, build, and run the project.
Create an empty folder on you hard-drive, right-click on an empty space and open a gitbash window
Clone the repository:
git clone https://github.com/ioanniskemerlis/FFA.gitcd FFAcd backendgradle buildgradle bootRunIf everything is correct, you should see:
Verify the backend:
The backend runs by default at http://localhost:8080.
Open your browser and navigate to Swagger API documentation: http://localhost:8080/swagger-ui/index.html
cd ../frontendnpm installng serveVerify the frontend:
The Angular application runs by default at http://localhost:4200. Open your browser and navigate to it.
cd backendgradle testThe test results should be at build/reports/tests/test/index.html
gradle jacocotestreportThe test results should be at build/reports/jacoco/test/html/index.html
The MongoDB URI and the SECRET_KEY for password hashing are both hardcoded in the respective classes. This was done for Ease of Review.
Never hardcode sensitive information in a repository. use enviromental variables to store them safely.
- Username:
testuser - Password:
Testuser1$
The controller layer is responsible for handling HTTP requests from the frontend, invoking services, and returning appropriate responses.
Handles user authentication and registration.
Issues JWT tokens for authenticated users.
Verifies authentication with test endpoints.
/api/auth/register: Registers a new user.
/api/auth/login: Authenticates a user and returns a JWT token.
/api/auth/test: Test endpoint to verify JWT authentication.
Manages CRUD operations for user incomes.
Handles pagination and data transformation (DTOs).
/api/incomes: Create a new income or fetch paginated incomes.
/api/incomes/{id}: Update or delete a specific income.
/api/incomes/all: Fetch all incomes for a user.
Manages CRUD operations for user expenses.
Handles pagination and data transformation (DTOs).
/api/expenses: Create a new expense or fetch paginated expenses.
/api/expenses/{id}: Update or delete a specific expense.
/api/expenses/all: Fetch all expenses for a user.
The service layer encapsulates business logic, interacting with repositories and managing application workflows.
Handles business logic related to user management.
Manages user registration with password hashing.
registerUser(User user): Hashes the password and saves the user to the database.
Provides business logic for managing incomes.
Interacts with IncomeRepository for CRUD operations.
addIncome(Income income): Adds a new income record.
getIncomesByUser(String userId, int page, int size): Fetches paginated incomes for a user.
updateIncome(String id, Income updatedIncome): Updates an existing income record.
deleteIncome(String id): Deletes an income record.
Provides business logic for managing expenses.
Interacts with ExpenseRepository for CRUD operations.
addExpense(Expense expense): Adds a new expense record.
getExpensesByUser(String userId, int page, int size): Fetches paginated expenses for a user.
updateExpense(String id, Expense updatedExpense): Updates an existing expense record.
deleteExpense(String id): Deletes an expense record.
Implements Spring Security’s UserDetailsService for authentication.
Loads user details (e.g., username, password, roles) from the database.
loadUserByUsername(String username): Fetches user details for authentication.
The repository layer interacts with the MongoDB database for CRUD operations.
Provides methods to query the users collection.
findByUsername(String username): Fetches a user by their username.
Provides methods to query the incomes collection.
findByUserId(String userId, Pageable pageable): Fetches paginated incomes for a user.
findAllIncomeByUserId(String userId): Fetches all incomes for a user.
Provides methods to query the expenses collection.
findByUserId(String userId, Pageable pageable): Fetches paginated expenses for a user.
findAllExpensesByUserId(String userId): Fetches all expenses for a user.
The model layer defines the data structure used across the application.
Represents the user entity stored in the users collection.
Includes fields like username, password, email, and role.
Annotations:
@Document(collection = "users"): Maps the class to the MongoDB collection.
Represents the income entity stored in the incomes collection.
Includes fields like type, amount, date, and notes.
Annotations:
@Document(collection = "incomes"): Maps the class to the MongoDB collection.
Represents the expense entity stored in the expenses collection.
Includes fields like type, amount, date, and notes.
Annotations:
@Document(collection = "expenses"): Maps the class to the MongoDB collection.
The utility layer provides reusable components that support application functionality.
Generates and validates JWT tokens for authentication.
Extracts the username from tokens.
generateToken(String username): Generates a JWT token for a username.
extractUsername(String token): Extracts the username from a JWT token.
validateToken(String token, String username): Validates the token for a username.
The security layer enforces authentication and authorization policies.
Configures Spring Security settings for the application.
Manages JWT filter integration and endpoint permissions.
CORS: Configures cross-origin requests for the Angular frontend.
JWT Authentication Filter: Validates tokens for protected endpoints.
CSRF Protection: Disabled to allow token-based authentication.
Intercepts HTTP requests to validate JWT tokens.
Populates the SecurityContextHolder with authentication details.
doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain): Validates JWT tokens for each request.
The mapper layer transforms data between entities and DTOs.
Converts between Income entities and IncomeRequestDTO/IncomeResponseDTO.
toEntity(IncomeRequestDTO dto, String userId): Converts a DTO to an entity.
toResponseDTO(Income income): Converts an entity to a DTO.
Converts between Expense entities and ExpenseRequestDTO/ExpenseResponseDTO.
toEntity(ExpenseRequestDTO dto, String userId): Converts a DTO to an entity.
toResponseDTO(Expense expense): Converts an entity to a DTO.
Package the Spring Boot application as a JAR file:
Deploy the JAR file to your hosting provider (e.g., AWS, Azure, Heroku) or a local server.
Build the production version of the Angular application:
ng build --prodDeploy the contents of the dist/ folder to a static hosting service or a web server.
Feel free to fork the repository and submit pull requests. Contributions are welcome! This repository is open-source, you have my permission to use for experimenting/learning