diff --git a/.github/workflows/prevent-env-file.yml b/.github/workflows/prevent-env-file.yml index a627e02..5814fc0 100644 --- a/.github/workflows/prevent-env-file.yml +++ b/.github/workflows/prevent-env-file.yml @@ -15,8 +15,8 @@ jobs: - name: Search for .env files run: | if git diff --name-only origin/main...HEAD | grep -E '(^|/)\.env'; then - echo "❌ PR contain .env file, merge is forbiden." + echo "This PR contains .env file(s), merge is forbiden." exit 1 else - echo "✅ There are no .env files in this PR-u." + echo "There are no .env files in this PR." fi diff --git a/Fitness/.vs/Fitness/DesignTimeBuild/.dtbcache.v2 b/Fitness/.vs/Fitness/DesignTimeBuild/.dtbcache.v2 deleted file mode 100644 index 03ecbd6..0000000 Binary files a/Fitness/.vs/Fitness/DesignTimeBuild/.dtbcache.v2 and /dev/null differ diff --git a/Fitness/.vs/Fitness/FileContentIndex/17d1ff65-554b-4af2-973f-854f10067e1b.vsidx b/Fitness/.vs/Fitness/FileContentIndex/17d1ff65-554b-4af2-973f-854f10067e1b.vsidx deleted file mode 100644 index 70aef67..0000000 Binary files a/Fitness/.vs/Fitness/FileContentIndex/17d1ff65-554b-4af2-973f-854f10067e1b.vsidx and /dev/null differ diff --git a/Fitness/.vs/Fitness/FileContentIndex/193bcdd9-f0a1-47bc-b932-e5151266b6ff.vsidx b/Fitness/.vs/Fitness/FileContentIndex/193bcdd9-f0a1-47bc-b932-e5151266b6ff.vsidx deleted file mode 100644 index a9a22cf..0000000 Binary files a/Fitness/.vs/Fitness/FileContentIndex/193bcdd9-f0a1-47bc-b932-e5151266b6ff.vsidx and /dev/null differ diff --git a/Fitness/.vs/Fitness/FileContentIndex/242fe2c1-e01c-46a3-a911-be2a37280c63.vsidx b/Fitness/.vs/Fitness/FileContentIndex/242fe2c1-e01c-46a3-a911-be2a37280c63.vsidx deleted file mode 100644 index 70aef67..0000000 Binary files a/Fitness/.vs/Fitness/FileContentIndex/242fe2c1-e01c-46a3-a911-be2a37280c63.vsidx and /dev/null differ diff --git a/Fitness/.vs/Fitness/FileContentIndex/4e99f751-4275-4778-b6c8-94bb6764fd71.vsidx b/Fitness/.vs/Fitness/FileContentIndex/4e99f751-4275-4778-b6c8-94bb6764fd71.vsidx deleted file mode 100644 index 70aef67..0000000 Binary files a/Fitness/.vs/Fitness/FileContentIndex/4e99f751-4275-4778-b6c8-94bb6764fd71.vsidx and /dev/null differ diff --git a/Fitness/.vs/Fitness/FileContentIndex/8db63fe7-992f-4011-97f0-b1cee3ec916e.vsidx b/Fitness/.vs/Fitness/FileContentIndex/8db63fe7-992f-4011-97f0-b1cee3ec916e.vsidx deleted file mode 100644 index 76329b5..0000000 Binary files a/Fitness/.vs/Fitness/FileContentIndex/8db63fe7-992f-4011-97f0-b1cee3ec916e.vsidx and /dev/null differ diff --git a/Fitness/.vs/Fitness/v17/.futdcache.v2 b/Fitness/.vs/Fitness/v17/.futdcache.v2 deleted file mode 100644 index a956b84..0000000 Binary files a/Fitness/.vs/Fitness/v17/.futdcache.v2 and /dev/null differ diff --git a/Fitness/.vs/Fitness/v17/.wsuo b/Fitness/.vs/Fitness/v17/.wsuo deleted file mode 100644 index dce3c7c..0000000 Binary files a/Fitness/.vs/Fitness/v17/.wsuo and /dev/null differ diff --git a/Fitness/.vs/Fitness/v17/DocumentLayout.json b/Fitness/.vs/Fitness/v17/DocumentLayout.json deleted file mode 100644 index 7fd6f39..0000000 --- a/Fitness/.vs/Fitness/v17/DocumentLayout.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "Version": 1, - "WorkspaceRootPath": "D:\\FitPlusPlus\\Fitness\\", - "Documents": [ - { - "AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|D:\\FitPlusPlus\\Fitness\\Backend\\Services\\TrainerService\\TrainerService.API\\Entities\\Trainer.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}", - "RelativeMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|solutionrelative:Backend\\Services\\TrainerService\\TrainerService.API\\Entities\\Trainer.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}" - } - ], - "DocumentGroupContainers": [ - { - "Orientation": 0, - "VerticalTabListWidth": 256, - "DocumentGroups": [ - { - "DockedWidth": 200, - "SelectedChildIndex": 0, - "Children": [ - { - "$type": "Document", - "DocumentIndex": 0, - "Title": "Trainer.cs", - "DocumentMoniker": "D:\\FitPlusPlus\\Fitness\\Backend\\Services\\TrainerService\\TrainerService.API\\Entities\\Trainer.cs", - "RelativeDocumentMoniker": "Backend\\Services\\TrainerService\\TrainerService.API\\Entities\\Trainer.cs", - "ToolTip": "D:\\FitPlusPlus\\Fitness\\Backend\\Services\\TrainerService\\TrainerService.API\\Entities\\Trainer.cs", - "RelativeToolTip": "Backend\\Services\\TrainerService\\TrainerService.API\\Entities\\Trainer.cs", - "ViewState": "AQIAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", - "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|", - "WhenOpened": "2024-08-09T12:48:07.265Z", - "EditorCaption": "" - } - ] - } - ] - } - ] -} \ No newline at end of file diff --git a/Fitness/.vs/VSWorkspaceState.json b/Fitness/.vs/VSWorkspaceState.json deleted file mode 100644 index e70d9ba..0000000 --- a/Fitness/.vs/VSWorkspaceState.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "ExpandedNodes": [ - "", - "\\Backend", - "\\Backend\\Services", - "\\Backend\\Services\\ClientService", - "\\Backend\\Services\\ClientService\\ClientService.API", - "\\Backend\\Services\\ClientService\\ClientService.API\\Controllers", - "\\Backend\\Services\\ClientService\\ClientService.API\\Data", - "\\Backend\\Services\\ClientService\\ClientService.API\\Entities", - "\\Backend\\Services\\TrainerService\\TrainerService.API", - "\\Backend\\Services\\TrainerService\\TrainerService.API\\Entities" - ], - "PreviewInSolutionExplorer": false -} \ No newline at end of file diff --git a/Fitness/.vs/slnx.sqlite b/Fitness/.vs/slnx.sqlite deleted file mode 100644 index 845a5ad..0000000 Binary files a/Fitness/.vs/slnx.sqlite and /dev/null differ diff --git a/Fitness/Backend/Services/NotificationService/NotificationService.API/.env.template b/Fitness/Backend/Services/NotificationService/NotificationService.API/.env.template new file mode 100644 index 0000000..252ccca --- /dev/null +++ b/Fitness/Backend/Services/NotificationService/NotificationService.API/.env.template @@ -0,0 +1 @@ +EMAIL_PASSWORD= \ No newline at end of file diff --git a/Fitness/Backend/Services/NotificationService/NotificationService.API/Controller/NotificationController.cs b/Fitness/Backend/Services/NotificationService/NotificationService.API/Controller/NotificationController.cs index 009db14..51ab317 100644 --- a/Fitness/Backend/Services/NotificationService/NotificationService.API/Controller/NotificationController.cs +++ b/Fitness/Backend/Services/NotificationService/NotificationService.API/Controller/NotificationController.cs @@ -6,6 +6,14 @@ namespace NotificationService.API.Controller; +/// +/// Provides REST API endpoints for fetching and modifying notification data. +/// +/// +/// This controller exposes operations for fetching and modyfing notification data. +/// +/// All routes are secured and require authorization with roles Admin, Trainer, or Client. +/// [Authorize] [ApiController] [Route("api/v1/[controller]")] @@ -18,6 +26,10 @@ public NotificationController(IRepository repository, IMapper mapper) _repository = repository ?? throw new ArgumentNullException(nameof(repository)); } + /// + /// API for fetching all notifications. + /// + /// IEnumerable of all notifications. [Authorize(Roles = "Admin")] [HttpGet] [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] @@ -27,6 +39,11 @@ public async Task>> GetNotifications() return Ok(notifications); } + /// + /// API for fetching all notifications associated with a given user. + /// + /// Unique identifier of the desired user + /// IEnumerable of notifications for the given user. [Authorize(Roles = "Admin, Trainer, Client")] [HttpGet("user/{userId}")] [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] @@ -36,6 +53,11 @@ public async Task>> GetNotificationsByUse return Ok(notifications); } + /// + /// API for fetching a notification with the given identifier. + /// + /// Unique identifier of the notification + /// Notification with the given identifier. [Authorize(Roles = "Admin, Trainer, Client")] [HttpGet("{id}")] [ProducesResponseType(typeof(Notification), StatusCodes.Status200OK)] @@ -45,6 +67,11 @@ public async Task> GetNotificationById(string id) return Ok(notification); } + /// + /// API for updating a notification. + /// + /// Notification object containing updated values. + /// Updated notification object. [Authorize(Roles = "Admin, Trainer, Client")] [HttpPut] [ProducesResponseType(typeof(Notification), StatusCodes.Status200OK)] @@ -53,7 +80,11 @@ public async Task UpdateNotification([FromBody] Notification noti return Ok(await _repository.UpdateNotification(notification)); } - // PUT api/notifications/{id}/read + /// + /// API for marking a notification (with the given identifier) as read. + /// + /// Unique identifier of the notification to be marked as read. + /// 200 OK if successful, 404 NotFound if notification does not exist. [Authorize(Roles = "Trainer, Client")] [HttpPut("{id}/read")] [ProducesResponseType(StatusCodes.Status200OK)] @@ -68,7 +99,10 @@ public async Task MarkAsRead(string id) return Ok(new { Message = "Notification marked as read" }); } - + /// + /// API for deleting all notifications. + /// + /// 200 OK when all notifications are deleted. [Authorize(Roles = "Admin")] [HttpDelete] [ProducesResponseType(typeof(Notification), StatusCodes.Status200OK)] @@ -78,14 +112,24 @@ public async Task DeleteNotifications() return Ok(); } + /// + /// API for deleting all notifications for a given user. + /// + /// Unique identifier of the user whose notifications should be deleted. + /// 200 OK when notifications are deleted. [Authorize(Roles = "Admin, Trainer, Client")] [HttpDelete("/user/{userId}")] [ProducesResponseType(typeof(Notification), StatusCodes.Status200OK)] public async Task DeleteNotificationsByUserTypeAndUserId(string userId) { - return Ok( await _repository.DeleteNotificationsByUserId(userId)); + return Ok(await _repository.DeleteNotificationsByUserId(userId)); } + /// + /// API for deleting a notification with the given identifier. + /// + /// Unique identifier of the notification. + /// 200 OK when the notification is deleted. [Authorize(Roles = "Admin, Trainer, Client")] [HttpDelete("{id}")] [ProducesResponseType(typeof(Notification), StatusCodes.Status200OK)] diff --git a/Fitness/Backend/Services/NotificationService/NotificationService.API/Email/EmailService.cs b/Fitness/Backend/Services/NotificationService/NotificationService.API/Email/EmailService.cs index e4f0b18..bd191ef 100644 --- a/Fitness/Backend/Services/NotificationService/NotificationService.API/Email/EmailService.cs +++ b/Fitness/Backend/Services/NotificationService/NotificationService.API/Email/EmailService.cs @@ -13,6 +13,9 @@ public EmailService(IFluentEmail fluentEmail) public async Task SendEmailAsync(string to, string subject, string body) { + // Clear all previous recipients -- ADDED AFTER THE DEADLINE -- minor addition + _fluentEmail.Data.ToAddresses.Clear(); + var response = await _fluentEmail .To(to) .Subject(subject) diff --git a/Fitness/Backend/Services/PaymentService/PaymentService.API/.env.template b/Fitness/Backend/Services/PaymentService/PaymentService.API/.env.template new file mode 100644 index 0000000..28e4d3b --- /dev/null +++ b/Fitness/Backend/Services/PaymentService/PaymentService.API/.env.template @@ -0,0 +1,2 @@ +PayPalSettings__ClientId= +PayPalSettings__ClientSecret= \ No newline at end of file diff --git a/Fitness/Backend/Services/ReservationService/ReservationService.API/Controllers/ReservationController.cs b/Fitness/Backend/Services/ReservationService/ReservationService.API/Controllers/ReservationController.cs index cf1ae97..cdc677d 100644 --- a/Fitness/Backend/Services/ReservationService/ReservationService.API/Controllers/ReservationController.cs +++ b/Fitness/Backend/Services/ReservationService/ReservationService.API/Controllers/ReservationController.cs @@ -7,6 +7,14 @@ namespace ReservationService.API.Controllers; +/// +/// Provides REST API endpoints for fetching, creating, and managing reservation data. +/// +/// +/// This controller exposes operations for handling both individual and group reservations. +/// +/// All routes are secured and require authorization with roles Admin, Trainer, or Client. +/// [Authorize] [ApiController] [Route("api/v1/[controller]")] @@ -16,9 +24,13 @@ public class ReservationController : ControllerBase public ReservationController(IReservationService reservationService) { - _reservationService = reservationService ?? throw new ArgumentNullException(nameof(reservationService)); + _reservationService = reservationService ?? throw new ArgumentNullException(nameof(reservationService)); } + /// + /// API for fetching all individual reservations. + /// + /// IEnumerable of all individual reservations. [Authorize(Roles = "Admin, Client, Trainer")] [HttpGet("individual")] [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] @@ -27,7 +39,11 @@ public async Task>> GetIndividua var reservations = await _reservationService.GetIndividualReservationsAsync(); return Ok(reservations); } - + + /// + /// API for fetching all group reservations. + /// + /// IEnumerable of all group reservations. [Authorize(Roles = "Admin, Client, Trainer")] [HttpGet("group")] [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] @@ -36,9 +52,14 @@ public async Task>> GetGroupReservati var reservations = await _reservationService.GetGroupReservationsAsync(); return Ok(reservations); } - + + /// + /// API for fetching a specific individual reservation by its identifier. + /// + /// Unique identifier of the individual reservation. + /// Individual reservation with the given identifier. [Authorize(Roles = "Admin, Client, Trainer")] - [HttpGet("individual/{id}", Name="GetIndividualReservation")] + [HttpGet("individual/{id}", Name = "GetIndividualReservation")] [ProducesResponseType(typeof(IndividualReservation), StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task> GetIndividualReservation(string id) @@ -47,7 +68,12 @@ public async Task> GetIndividualReservation( if (reservation == null) return NotFound(); return Ok(reservation); } - + + /// + /// API for fetching a specific group reservation by its identifier. + /// + /// Unique identifier of the group reservation. + /// Group reservation with the given identifier. [Authorize(Roles = "Admin, Client, Trainer")] [HttpGet("group/{id}", Name = "GetGroupReservation")] [ProducesResponseType(typeof(GroupReservation), StatusCodes.Status200OK)] @@ -58,7 +84,12 @@ public async Task> GetGroupReservation(string id) if (reservation == null) return NotFound(); return Ok(reservation); } - + + /// + /// API for fetching all individual reservations for a specific client. + /// + /// Unique identifier of the client. + /// IEnumerable of individual reservations for the given client. [Authorize(Roles = "Admin, Client, Trainer")] [HttpGet("individual/client/{clientId}")] [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] @@ -68,6 +99,11 @@ public async Task>> GetIndividua return Ok(reservations); } + /// + /// API for fetching all group reservations for a specific client. + /// + /// Unique identifier of the client. + /// IEnumerable of group reservations for the given client. [Authorize(Roles = "Admin, Client, Trainer")] [HttpGet("group/client/{clientId}")] [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] @@ -77,6 +113,11 @@ public async Task>> GetGroupReservati return Ok(reservations); } + /// + /// API for fetching all individual reservations for a specific trainer. + /// + /// Unique identifier of the trainer. + /// IEnumerable of individual reservations for the given trainer. [Authorize(Roles = "Admin, Client, Trainer")] [HttpGet("individual/trainer/{trainerId}")] [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] @@ -85,7 +126,12 @@ public async Task>> GetIndividua var reservations = await _reservationService.GetIndividualReservationsByTrainerIdAsync(trainerId); return Ok(reservations); } - + + /// + /// API for fetching all group reservations for a specific trainer. + /// + /// Unique identifier of the trainer. + /// IEnumerable of group reservations for the given trainer. [Authorize(Roles = "Admin, Client, Trainer")] [HttpGet("group/trainer/{trainerId}")] [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] @@ -94,7 +140,12 @@ public async Task>> GetGroupReservati var reservations = await _reservationService.GetGroupReservationsByTrainerIdAsync(trainerId); return Ok(reservations); } - + + /// + /// API for creating a new individual reservation. + /// + /// Individual reservation data to be created. + /// 201 Created if successful. [Authorize(Roles = "Client")] [HttpPost("individual")] [ProducesResponseType(typeof(IndividualReservation), StatusCodes.Status201Created)] @@ -111,6 +162,11 @@ public async Task> CreateIndividualReservati } } + /// + /// API for creating a new group reservation. + /// + /// Group reservation data to be created. + /// 201 Created if successful. [Authorize(Roles = "Trainer")] [HttpPost("group")] [ProducesResponseType(typeof(GroupReservation), StatusCodes.Status201Created)] @@ -127,12 +183,17 @@ public async Task> CreateGroupReservation([FromBo } } + /// + /// API for deleting a group reservation by its identifier. + /// + /// Unique identifier of the group reservation. + /// 204 NoContent if deleted successfully. [Authorize(Roles = "Trainer")] [HttpDelete("group/{id}")] [ProducesResponseType(typeof(GroupReservation), StatusCodes.Status200OK)] public async Task DeleteGroupReservation(string id) { - var deleted = await _reservationService.DeleteGroupReservationAsync(id); + var deleted = await _reservationService.DeleteGroupReservationAsync(id); if (deleted) { return NoContent(); @@ -142,7 +203,12 @@ public async Task DeleteGroupReservation(string id) return BadRequest(); } } - + + /// + /// API for cancelling an individual reservation by the client. + /// + /// Unique identifier of the reservation to cancel. + /// 204 NoContent if cancelled successfully. [Authorize(Roles = "Client")] [HttpPut("individual/client/cancel/{id}")] [ProducesResponseType(typeof(IndividualReservation), StatusCodes.Status204NoContent)] @@ -152,15 +218,26 @@ public async Task CancelClientIndividualReservation(string id) return cancelled ? Ok(cancelled) : BadRequest(); } + /// + /// API for cancelling an individual reservation by the trainer. + /// + /// Unique identifier of the reservation to cancel. + /// 204 NoContent if cancelled successfully. [Authorize(Roles = "Trainer")] [HttpPut("individual/trainer/cancel/{id}")] [ProducesResponseType(typeof(IndividualReservation), StatusCodes.Status204NoContent)] public async Task CancelTrainerIndividualReservation(string id) { - var cancelled = await _reservationService.TrainerCancelIndividualReservationAsync(id); + var cancelled = await _reservationService.TrainerCancelIndividualReservationAsync(id); return cancelled ? Ok(cancelled) : BadRequest(); } + /// + /// API for booking a group reservation by a client. + /// + /// Unique identifier of the group reservation. + /// Unique identifier of the client booking the reservation. + /// 200 OK if booked successfully. [Authorize(Roles = "Client")] [HttpPost("group/book/{id}")] [ProducesResponseType(typeof(GroupReservation), StatusCodes.Status200OK)] @@ -177,6 +254,12 @@ public async Task BookGroupReservation(string id, [FromQuery] str } } + /// + /// API for cancelling a group reservation by a client. + /// + /// Unique identifier of the group reservation. + /// Unique identifier of the client cancelling the reservation. + /// 204 NoContent if cancelled successfully. [Authorize(Roles = "Client")] [HttpPost("group/cancel/{id}")] [ProducesResponseType(typeof(GroupReservation), StatusCodes.Status204NoContent)] diff --git a/README.md b/README.md index 3f68208..5debc0d 100644 --- a/README.md +++ b/README.md @@ -20,9 +20,9 @@ This project is an **extension of the previous FitPlusPlus application**, which ### Previous Development Team (2023): -3. **Belaković Nikola** – Student ID: 1023/2023 -1. **Stanojević Lazar** – Student ID: 1013/2023 -2. **Todorović Vasilije** – Student ID: 1015/2023 +1. **Belaković Nikola** – Student ID: 1023/2023 +2. **Stanojević Lazar** – Student ID: 1013/2023 +3. **Todorović Vasilije** – Student ID: 1015/2023 GitHub Repository of the Previous Project: [FitPlusPlus](https://github.com/lazars01/FitPlusPlus) @@ -44,7 +44,7 @@ The FitPlusPlus application consists of multiple microservices, some developed b - Manages trainer profiles, schedules, and training history. 4. **ReviewService** - - Allows clients to leave reviews and ratings for trainers. + - Allows clients and trainers to leave reviews for each training. 5. **PaymentService** - Processes payments for training sessions. @@ -84,7 +84,11 @@ The FitPlusPlus application consists of multiple microservices, some developed b 3. **ReservationService** - Enables **booking of individual and group training sessions**. - Supports **real-time scheduling, cancellation, and availability tracking**. - - Integrated with NotificationService for real-time and email notifications on reservation updates. + - Integrated with: + - PaymentService, for real-time payments of training sessions + - NotificationService, for real-time and email notifications on reservation updates. + - AnalyticsService, for real-time updates of various training-related statistics of clients and trainers. + 4. **NotificationService** - Sends **push and email notifications** to clients and trainers. @@ -103,8 +107,8 @@ The FitPlusPlus application consists of multiple microservices, some developed b - **Charts** visualizing collaboration between clients and trainers 6. **Gateway and Discovery Service** - - A **centralized API gateway** that directs requests to the correct microservice. - - Facilitates **automatic detection and scaling** of microservices. + - A **centralized API gateway** that directs requests to the correct microservice, using **Ocelot** - an API Gateway library. + - Facilitates **automatic detection and scaling** of microservices, using **Consul** - a Service Discovery library. 7. **Nutrition Service** - Manages **nutrition goals, meal plans and calorie tracking** for clients and trainers. @@ -122,7 +126,7 @@ The FitPlusPlus application consists of multiple microservices, some developed b - Interactive Calorie Tracker: Client can enter food in grams and automatically calculates consumed and remaining calories. - This **[video](https://youtu.be/nSJhnaC0eAc?si=-Dm2DyJzZ1tnmKfo)** demonstrates the capabilities of the **Nutrition Service**: https://youtu.be/nSJhnaC0eAc?si=-Dm2DyJzZ1tnmKfo + This **[video](https://youtu.be/nSJhnaC0eAc?si=-Dm2DyJzZ1tnmKfo)** demonstrates the capabilities of the **Nutrition Service**. ![Adobe Express - file](https://github.com/user-attachments/assets/0ffc42e1-89b1-4107-938d-277fea981f7a) --- @@ -136,6 +140,8 @@ The FitPlusPlus application consists of multiple microservices, some developed b - **Event Bus:** RabbitMQ for microservices communication - **Communication Protocols:** WebSockets (real-time chat), GRPC and REST API for microservices communication - **Payment Integration:** PayPal for chat session payments and booking training +- **Gateway and Service Discovery**: Ocelot (API Gateway), Consul (Service Discovery) +- **Emailing**: FluentEmail (using SMTP protocol) ## Platform Compatibility @@ -159,20 +165,22 @@ To start the updated application: cd FitPlusPlus ``` -2. Start all microservices from FitPlusPlus/Fitness/Backend: +2. Create `.env` files, based on templates given in various `.env.template` files. Fill in the fields with your secrets. Required for PaymentService and NotificationService. *Note: For changing the configuration for sending out email notifications, change the configuration variables specified in `docker-compose.development.yml` under the `notificationservice.api` part. Email configuration variables start with `EmailSettings`* + +3. Start all microservices from FitPlusPlus/Fitness/Backend: ```bash docker-compose -f docker-compose.yml -f docker-compose.development.yml up -d --build ``` -3. Start the frontend service from FitPlusPlus/Fitness/Frontend directory: +4. Start the frontend service from FitPlusPlus/Fitness/Frontend directory: ```bash npm install npm run serve ``` -4. Open the application in your browser: +5. Open the application in your browser: ``` http://localhost:8080