diff --git a/README.md b/README.md index 955bbd59..ed0cf2fc 100644 --- a/README.md +++ b/README.md @@ -1,108 +1,82 @@ -# Voting System - -## Overview -The Voting System is a platform for secure and anonymous voting during meetings. -It uses a microservices architecture with the following main services: - -- **Housing Association Service**: - - Retrieve housing association details - - Add and list residents - - Send meeting invitations to residents via RabbitMQ - - Generate resident/housing reports - -- **Meeting Service**: - - Manage meetings and participants - - Create, start, and end meetings - - Add participants and manage meeting status - - Start question rounds - - Provide meeting details to frontend and other microservices - -- **Invitation Service**: - - Handle invitation codes - - Validate participants - - Manage resident access to meetings by accepting invitations via API endpoints - -- **Voting Service**: - - Create new voting sessions - - Retrieve voting details - - Update participant votes - - Track voting state - - Generate internal reports for meetings - -- **Notification Service**: - - Manage and provide notifications for residents - - Expose endpoints to retrieve all notifications for a specific resident - -- **Report Service**: - - Generate and provide reports for meetings after they end - - Fetch a specific report by its code (used by the Meeting microservice) - - Export reports in CSV/PDF format - -The system uses RabbitMQ for communication between services. -Voters access the system via links, and admins can view and export results. - -## System Design - -This section covers the planned architecture, main features, and user stories for the Voting System. -It includes high-level decisions on microservices, communication patterns, and the main functionality of each service. - -### Architectural Decisions -- Microservices architecture for modularity and scalability -- RabbitMQ for asynchronous communication between services -- Secure and anonymous voting flow -- Admin access for managing meetings and exporting reports - -### Features -- Housing Association management -- Meeting management -- Invitations and participant validation -- Voting sessions with anonymous votes -- Notifications for residents -- Report generation and export - -### User Stories -1. **Voter – Invite and Voting** - - Access voting page via link - - Cast votes for meeting questions - - One vote per question - - Confirmation of vote - -2. **Admin – Voting Results** - - Secure login - - Register participants - - View and export results - - Votes remain anonymous - -## Architecture Diagram - -### Start Meeting Flow -![Start Meeting Flow](./docs/createmeeting.png) - -### End Meeting Flow -*Diagram TBD* - -## Build and Run Instructions -Option 1: -- `docker compose -f docker-compose.dev.yml up` - -## Team Contributions -- **Architecture & Planning:** Mohemmed, Wisdan, Mustafa - - Discussed system design, microservices architecture, communication patterns, and feature planning - -- **Backend development:** - - Mohemmed: Gateway, Meeting Service, Notification Service and Report Service - - Wisdan: Voting Service, Report Service, Notification Service - - Mustafa: Housing Association Service - -- **Frontend development:** Mohemmed, Wisdan, - - Voting UI, Login UI, Meeting UI - -- **Authentication:** Mohemmed - -- **Docker setup & deployment:** Mohemmed, Wisdan - -## Decisions and Simplifications -- MVP focuses on core voting and meeting results; proxy not included -- Added Report and Notification services beyond initial plan -- Security is implemented via Spring Boot Security -- Integrated RabbitMQ for asynchronous communication between services, exceeding initial scope +# Exam assigment - PG3402 Microservices + +**Group Members:** +- Mohemmed Mossa Abdul-riza +- Wisdan Anes +- Mustafa Buga + +**[See overview of the project ](docs/README.md)** + +--- +- [x] Instructions on how to build, start, and run the project. if you have more than one means of running the project (for example, docker + containers run locally, docker containers running on dockerhub, or running the individual + services locally), make sure you have clear instructions for each of these. + + +- [x] An overview of the project (you can use the one from the arbeidskrav as a basis). The + overview should accurately describe the project implementation. + + +- [x] A list of user stories that allow an examiner to assess the functionality developed in the + project (think of the most interesting and important scenarios that the examiner can run to see + what functionality you have implemented). + + +- [ ] A diagram showing the architecture of your system. This should show what services the + project contains and what type of communication they have between them (synchronous or + asynchronous). + + +- [x] if you collaborated with other students on a project, the README.md file should also + contain a discussion of the contributions and responsibilities of each team member. + During development, you may have to make a number of architecture decisions, assumptions + about the domain you are working with, or simplifications to how such a project would work + in reality. Document these decisions, assumptions, and simplifications in the README.md + file. + +--- +### Microservices specific requirements +#### Required (but not sufficient) for E + +- [x] Use multiple services, that fulfill different functionality and communicate with each +other + +- [x] At least two of the services communicate using synchronous communication (for +example, direct REST calls between two services). + +- [x] At least two of the services communicate using asynchronous communication (for +example, using Message Queue). This will be done in accordance with event-driven +architecture, as discussed in class. + +Required (but not sufficient) for D + +- [x] Each service has a clear structure and functionality. + +- [x] The architecture of the project is consistent with the documentation. (All services are +described in the architecture, they can be found in the project, their functionality, structure, +interactions are documented. + +- [x] The project can be deployed as a group of Docker containers. + +Required (but not sufficient) for C + +- [x] The project uses a unique access point, that handled calls and routes them to appropriate +services – Gateway + +- [x] The project uses a unique access point that, in addition to routing calls, also does load +balancing + +Required (but not sufficient) for B +- [x] The project has a means of centrally controlling the health of running services – health +check + +- [x] The project, and all services and necessary components, can be started via docker +compose (using the command ‘docker compose up’). The project is ready for integration into +a CI/CD pipeline. + +Required (but not sufficient) for A +- [ ] The project has a means of centrally controlling configurations for the services – for +example, using Consul + +- [x] The project can be deployed with multiple instances of relevant services of the +services. diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 00000000..cd54f671 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,110 @@ +# Voting System + +## Overview +The Voting System is a platform for secure and anonymous voting during meetings. +It uses a microservices architecture with the following main services: + +See the [Housing Service Documentation](../housing_association/README.md). + +See the [Meeting Service Documentation](../meeting/README.md). + +See the [Invitation Service Documentation](../invitation/README.md). + +See the [Voting Service Documentation](../voting/README.md). + +See the [Notification Service Documentation](../notification/README.md). + +See the [Report Service Documentation](../report/README.md). + +The system uses RabbitMQ for communication between services. +Voters access the system via links, and admins can view and export results. + +--- +## System Design + +This section covers the planned architecture, main features, and user stories for the Voting System. +It includes high-level decisions on microservices, communication patterns, and the main functionality of each service. + +--- +### Architectural Decisions +- Microservices architecture for modularity and scalability +- RabbitMQ for asynchronous communication between services +- Secure and anonymous voting flow +- Admin access for managing meetings and exporting reports + +--- +### Features +- Housing Association management +- Meeting management +- Invitations and participant validation +- Voting sessions with anonymous votes +- Notifications for residents +- Report generation and export + +--- +### User Stories +1. **Voter – Invite and Voting** + - Access voting page via link + - Cast votes for meeting questions + - One vote per question + - Confirmation of vote + +2. **Admin – Voting Results** + - Secure login + - Register participants + - View and export results + - Votes remain anonymous + +--- +## Architecture Diagram + +### Create Meeting Flow +**Description:** +This flow illustrates how an admin creates a new meeting. + +Create Meeting Flow + +--- +### Start Meeting Flow +Start Meeting Flow + +--- +### Start Voting Flow +Start Voting Flow + +--- +### End Meeting Flow +End Meeting Flow + +--- +### Accept Invitation Flow +Accept Invitation Flow + +--- +## Build and Run Instructions +Option 1: +- `docker compose -f docker-compose.prod.yml up` + +--- +## Team Contributions +- **Architecture & Planning:** Mohemmed, Wisdan, Mustafa + - Discussed system design, microservices architecture, communication patterns, and feature planning + +- **Backend development:** + - Mohemmed: Gateway, Meeting Service, Notification Service and Report Service + - Wisdan: Voting Service, Report Service, Notification Service + - Mustafa: Housing Association Service + +- **Frontend development:** Mohemmed, Wisdan, + - Voting UI, Login UI, Meeting UI + +- **Authentication:** Mohemmed + +- **Docker setup & deployment:** Mohemmed, Wisdan + +--- +## Decisions and Simplifications +- MVP focuses on core voting and meeting results; proxy not included +- Added Report and Notification services beyond initial plan +- Security is implemented via Spring Boot Security +- Integrated RabbitMQ for asynchronous communication between services, exceeding initial scope diff --git a/docs/createmeeting.png b/docs/createmeeting.png deleted file mode 100644 index 2756dcf3..00000000 Binary files a/docs/createmeeting.png and /dev/null differ diff --git a/docs/images/database/housing_association.png b/docs/images/database/housing_association.png new file mode 100644 index 00000000..8ffbc687 Binary files /dev/null and b/docs/images/database/housing_association.png differ diff --git a/docs/images/database/invitation.png b/docs/images/database/invitation.png new file mode 100644 index 00000000..c758d06f Binary files /dev/null and b/docs/images/database/invitation.png differ diff --git a/docs/images/database/meeting.png b/docs/images/database/meeting.png new file mode 100644 index 00000000..08a8d686 Binary files /dev/null and b/docs/images/database/meeting.png differ diff --git a/docs/images/database/voting.png b/docs/images/database/voting.png new file mode 100644 index 00000000..52350c6a Binary files /dev/null and b/docs/images/database/voting.png differ diff --git a/docs/images/flow/acceptinvitation.png b/docs/images/flow/acceptinvitation.png new file mode 100644 index 00000000..180a19ca Binary files /dev/null and b/docs/images/flow/acceptinvitation.png differ diff --git a/docs/images/flow/createmeeting.png b/docs/images/flow/createmeeting.png new file mode 100644 index 00000000..dd72a9ee Binary files /dev/null and b/docs/images/flow/createmeeting.png differ diff --git a/docs/images/flow/endmeeting.png b/docs/images/flow/endmeeting.png new file mode 100644 index 00000000..d45e9461 Binary files /dev/null and b/docs/images/flow/endmeeting.png differ diff --git a/docs/images/flow/startmeeting.png b/docs/images/flow/startmeeting.png new file mode 100644 index 00000000..7f1c686c Binary files /dev/null and b/docs/images/flow/startmeeting.png differ diff --git a/docs/images/flow/startvoting.png b/docs/images/flow/startvoting.png new file mode 100644 index 00000000..eb8d8676 Binary files /dev/null and b/docs/images/flow/startvoting.png differ diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 0e8450e1..b61f64e9 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -1,10 +1,10 @@ import { BrowserRouter, Route, Routes } from "react-router-dom"; -import { Login } from "./pages/Login.tsx"; +import { Login } from "./login/Login.tsx"; import { Main } from "./pages/Main.tsx"; -import {Phone} from "./pages/Phone.tsx"; -import { Voting } from "./pages/Voting.tsx"; +import {Phone} from "./phone/Phone.tsx"; +import { Voting } from "./voting/Voting.tsx"; import {Error} from "./http/Error.tsx"; -import {Invitation} from "./pages/Invitation.tsx"; +import {Invitation} from "./invitation/Invitation.tsx"; import {useState} from "react"; import {PrivateRouter} from "./PrivateRouter.tsx"; import {Report} from "./pages/Report.tsx"; diff --git a/frontend/src/pages/Invitation.module.css b/frontend/src/invitation/Invitation.module.css similarity index 100% rename from frontend/src/pages/Invitation.module.css rename to frontend/src/invitation/Invitation.module.css diff --git a/frontend/src/pages/Invitation.tsx b/frontend/src/invitation/Invitation.tsx similarity index 100% rename from frontend/src/pages/Invitation.tsx rename to frontend/src/invitation/Invitation.tsx diff --git a/frontend/src/pages/Login.module.css b/frontend/src/login/Login.module.css similarity index 100% rename from frontend/src/pages/Login.module.css rename to frontend/src/login/Login.module.css diff --git a/frontend/src/pages/Login.tsx b/frontend/src/login/Login.tsx similarity index 100% rename from frontend/src/pages/Login.tsx rename to frontend/src/login/Login.tsx diff --git a/frontend/src/pages/Main.tsx b/frontend/src/pages/Main.tsx index 5c3f5d24..cd15ff8d 100644 --- a/frontend/src/pages/Main.tsx +++ b/frontend/src/pages/Main.tsx @@ -1,7 +1,7 @@ import { Button } from "@mui/material"; import css from "./Main.module.css"; import { useState } from "react"; -import { Resident } from "./Resident.tsx"; +import { Resident } from "../resident/Resident.tsx"; import * as React from "react"; import {Meeting} from "../meeting/Meeting.tsx"; import type {HousingAssociationDTO} from "../App.tsx"; diff --git a/frontend/src/pages/Profile.tsx b/frontend/src/pages/Profile.tsx index 8f1b8f75..8121abd6 100644 --- a/frontend/src/pages/Profile.tsx +++ b/frontend/src/pages/Profile.tsx @@ -2,7 +2,7 @@ import { useEffect, useState } from "react"; import { Button } from "@mui/material"; import * as http from "../http/HttpRequest.ts"; import { ClearTokenInSession, GetTokenInSession } from "../http/TokenStorage.ts"; -import css from "./Resident.module.css"; +import css from "../resident/Resident.module.css"; export interface HousingAssociationDTO { id: number; diff --git a/frontend/src/pages/Report.tsx b/frontend/src/pages/Report.tsx index 5b124410..e5a05d20 100644 --- a/frontend/src/pages/Report.tsx +++ b/frontend/src/pages/Report.tsx @@ -1,4 +1,4 @@ -import css from "./Resident.module.css"; +import css from "../resident/Resident.module.css"; import { useEffect, useState } from "react"; import { useSearchParams } from "react-router-dom"; import * as http from "../http/HttpRequest.ts"; diff --git a/frontend/src/pages/Phone.module.css b/frontend/src/phone/Phone.module.css similarity index 100% rename from frontend/src/pages/Phone.module.css rename to frontend/src/phone/Phone.module.css diff --git a/frontend/src/pages/Phone.tsx b/frontend/src/phone/Phone.tsx similarity index 100% rename from frontend/src/pages/Phone.tsx rename to frontend/src/phone/Phone.tsx diff --git a/frontend/src/pages/Resident.module.css b/frontend/src/resident/Resident.module.css similarity index 100% rename from frontend/src/pages/Resident.module.css rename to frontend/src/resident/Resident.module.css diff --git a/frontend/src/pages/Resident.tsx b/frontend/src/resident/Resident.tsx similarity index 100% rename from frontend/src/pages/Resident.tsx rename to frontend/src/resident/Resident.tsx diff --git a/frontend/src/pages/Voting.module.css b/frontend/src/voting/Voting.module.css similarity index 100% rename from frontend/src/pages/Voting.module.css rename to frontend/src/voting/Voting.module.css diff --git a/frontend/src/pages/Voting.tsx b/frontend/src/voting/Voting.tsx similarity index 90% rename from frontend/src/pages/Voting.tsx rename to frontend/src/voting/Voting.tsx index 86f02111..f9371075 100644 --- a/frontend/src/pages/Voting.tsx +++ b/frontend/src/voting/Voting.tsx @@ -1,9 +1,9 @@ import {useEffect, useRef, useState} from "react"; import css from "./Voting.module.css"; import * as http from "../http/HttpRequest.ts"; -import {VotingWaiting} from "../voting/VotingWaiting.tsx"; -import {VotingAnswerQuestion} from "../voting/VotingAnswerQuestion.tsx"; -import {VotingResult} from "../voting/VotingResult.tsx"; +import {VotingWaiting} from "./VotingWaiting.tsx"; +import {VotingAnswerQuestion} from "./VotingAnswerQuestion.tsx"; +import {VotingResult} from "./VotingResult.tsx"; export type SelectedVote = "FOR" | "AGAINST"| "NOT_VOTED"; export type VotingState = "MEETING_NOT_STARTED" | "NOT_ONGOING" | "ONGOING_BUT_YOU_HAVE_VOTED" | "ONGOING_BUT_YOU_HAVE_NOT_VOTED" | "MEETING_ENDED"; @@ -53,11 +53,11 @@ export function Voting() {
{/* Single Card containing everything */}
- {currentStatus === "MEETING_NOT_STARTED" && } + {currentStatus === "MEETING_NOT_STARTED" && } {currentStatus === "NOT_ONGOING" && } {currentStatus === "ONGOING_BUT_YOU_HAVE_NOT_VOTED" && } {currentStatus === "ONGOING_BUT_YOU_HAVE_VOTED" && } - {currentStatus === "MEETING_ENDED" && } + {currentStatus === "MEETING_ENDED" && }
diff --git a/frontend/src/voting/VotingAnswerQuestion.tsx b/frontend/src/voting/VotingAnswerQuestion.tsx index b122f7f8..25b502d5 100644 --- a/frontend/src/voting/VotingAnswerQuestion.tsx +++ b/frontend/src/voting/VotingAnswerQuestion.tsx @@ -1,5 +1,5 @@ -import css from "../pages/Voting.module.css"; -import type {SelectedVote} from "../pages/Voting.tsx"; +import css from "./Voting.module.css"; +import type {SelectedVote} from "./Voting.tsx"; import {useEffect, useState} from "react"; import * as http from "../http/HttpRequest.ts"; import {VotingWaiting} from "./VotingWaiting.tsx"; diff --git a/frontend/src/voting/VotingResult.tsx b/frontend/src/voting/VotingResult.tsx index 70772195..3a12f493 100644 --- a/frontend/src/voting/VotingResult.tsx +++ b/frontend/src/voting/VotingResult.tsx @@ -1,7 +1,7 @@ import * as http from "../http/HttpRequest.ts"; import css from "./VotingResult.module.css" import {useEffect, useState} from "react"; -import type {SelectedVote, VotingState} from "../pages/Voting.tsx"; +import type {SelectedVote, VotingState} from "./Voting.tsx"; import {Loading} from "../loading/Loading.tsx"; interface ApiVotingResult { diff --git a/frontend/src/voting/VotingWaiting.tsx b/frontend/src/voting/VotingWaiting.tsx index 49ae332d..86301cd5 100644 --- a/frontend/src/voting/VotingWaiting.tsx +++ b/frontend/src/voting/VotingWaiting.tsx @@ -2,11 +2,12 @@ import {Loading} from "../loading/Loading.tsx"; interface Props{ message:string + displayLoading?:boolean } -export function VotingWaiting({message}:Props){ +export function VotingWaiting({message, displayLoading=true}:Props){ return <>

{message}

- + {displayLoading? : <>} } \ No newline at end of file diff --git a/gateway/README.md b/gateway/README.md new file mode 100644 index 00000000..e69de29b diff --git a/housing_association/README.md b/housing_association/README.md new file mode 100644 index 00000000..ca79d6bc --- /dev/null +++ b/housing_association/README.md @@ -0,0 +1,8 @@ +# Housing Association Service + +- Retrieve housing association details +- Add and list residents +- Send meeting invitations to residents via RabbitMQ +- Generate resident/housing reports + +![housing_association](../docs/housing_association.png) \ No newline at end of file diff --git a/invitation/README.md b/invitation/README.md new file mode 100644 index 00000000..178863e7 --- /dev/null +++ b/invitation/README.md @@ -0,0 +1,7 @@ +# Invitation Service: +- Handle invitation codes +- Validate participants +- Manage resident access to meetings by accepting invitations via API endpoints + +### Database +![invitation](../docs/invitation.png) diff --git a/meeting/README.md b/meeting/README.md new file mode 100644 index 00000000..dac59ee3 --- /dev/null +++ b/meeting/README.md @@ -0,0 +1,10 @@ +- **Meeting Service**: + - Manage meetings and participants + - Create, start, and end meetings + - Add participants and manage meeting status + - Start question rounds + - Provide meeting details to frontend and other microservices + + +### Database +![meeting](../docs/meeting.png) \ No newline at end of file diff --git a/notification/README.md b/notification/README.md new file mode 100644 index 00000000..e4615729 --- /dev/null +++ b/notification/README.md @@ -0,0 +1,3 @@ +# Notification Service: +- Manage and provide notifications for residents +- Expose endpoints to retrieve all notifications for a specific resident \ No newline at end of file diff --git a/report/README.md b/report/README.md new file mode 100644 index 00000000..852b3bb0 --- /dev/null +++ b/report/README.md @@ -0,0 +1,9 @@ +# Report Service: +- Generate and provide reports for meetings after they end +- Fetch a specific report by its code (used by the Meeting microservice) +- Export reports in CSV/PDF format + +## Database: + +![Report table](./docs/report.png) + diff --git a/voting/README.md b/voting/README.md new file mode 100644 index 00000000..4a771210 --- /dev/null +++ b/voting/README.md @@ -0,0 +1,9 @@ +# Voting Service: +- Create new voting sessions +- Retrieve voting details +- Update participant votes +- Track voting state +- Generate internal reports for meetings + +### Database +![voting](../docs/voting.png) \ No newline at end of file