From 8ae2f323af0d3074babb4ae4b2ee4c312476326a Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 19 Feb 2026 23:57:41 +0000
Subject: [PATCH 1/4] Initial plan
From 47052078cb71af2216d0701a006b9997573d652d Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Fri, 20 Feb 2026 00:08:24 +0000
Subject: [PATCH 2/4] Merge master into copilot/sub-pr-567 - resolve conflicts
for CourseMentor model
Co-authored-by: DedSec256 <26364714+DedSec256@users.noreply.github.com>
---
.github/workflows/build.yml | 28 +
.github/workflows/deploy.yml | 2 +
.gitignore | 1 +
Directory.Build.props | 2 +-
.../ApplicationProfile.cs | 18 +-
.../Controllers/AccountController.cs | 26 +-
.../Controllers/AggregationController.cs | 57 +-
.../Controllers/CourseGroupsController.cs | 161 +-
.../Controllers/CoursesController.cs | 495 +-
.../Controllers/ExpertsController.cs | 205 +-
.../Controllers/FilesController.cs | 129 +-
.../Controllers/HomeworksController.cs | 101 +-
.../Controllers/NotificationsController.cs | 99 +-
.../Controllers/SolutionsController.cs | 894 +-
.../Controllers/StatisticsController.cs | 239 +-
.../Controllers/SystemController.cs | 107 +-
.../Controllers/TasksController.cs | 154 +-
.../HwProj.APIGateway.API/Dockerfile | 5 +-
.../ForbiddenExceptionFilter.cs | 18 +-
.../Filters/CourseMentorOnlyAttribute.cs | 106 +-
.../Filters/FilesCountLimiter.cs | 27 +
.../Filters/FilesPrivacyFilter.cs | 71 +
.../HwProj.APIGateway.API.csproj | 21 +-
.../Models/CoursePreviewView.cs | 35 +-
.../Models/Solutions/PostSolutionModel.cs | 55 +
.../Models/Solutions/SolutionPreviewView.cs | 40 +-
.../Models/Solutions/UserTaskSolutions.cs | 105 +-
.../AdvancedCourseStatisticsViewModel.cs | 19 +-
.../Statistics/StatisticsCourseMatesModel.cs | 17 +-
.../Statistics/StatisticsLecturersModel.cs | 13 +-
.../Models/SystemInfo.cs | 11 +-
.../Models/Tasks/TaskDeadlineView.cs | 17 +-
.../Models/UserDataDto.cs | 13 +-
.../HwProj.APIGateway.API/Program.cs | 32 +-
.../HwProj.APIGateway.API/Startup.cs | 168 +-
.../HwProj.APIGateway.API/appsettings.json | 5 +-
.../ApplicationProfile.cs | 1 +
.../Controllers/AccountController.cs | 17 +-
.../HwProj.AuthService.API/Dockerfile | 10 +-
.../Extensions/MappingExtensions.cs | 23 +
.../HwProj.AuthService.API.csproj | 23 +-
.../Models/ExpertData.cs | 3 +-
.../Models/IdentityContext.cs | 3 +-
.../HwProj.AuthService.API/Models/User.cs | 25 +
.../Repositories/ExpertsRepository.cs | 4 +-
.../Repositories/IExpertsRepository.cs | 4 +-
.../HwProj.AuthService.API/RoleInitializer.cs | 14 +-
.../Services/AccountService.cs | 11 +-
.../Services/AuthTokenService.cs | 2 +-
.../Services/ExpertsService.cs | 6 +-
.../Services/IAccountService.cs | 1 +
.../Services/IAuthTokenService.cs | 2 +-
.../Services/IUserManager.cs | 2 +-
.../Services/ProxyUserManager.cs | 2 +-
.../HwProj.AuthService.API/Startup.cs | 73 +-
.../HwProj.AuthService.API/appsettings.json | 2 +-
.../AuthServiceClient.cs | 8 +-
.../IAuthServiceClient.cs | 2 +-
.../AuthServiceTests.cs | 11 +-
.../HwProj.Common.Net8/ConnectionString.cs | 16 +
.../HwProj.Common.Net8.csproj | 14 +
.../HwProj.Common.Net8/StartupExtensions.cs | 38 +
.../ViewModels/RegisterExpertViewModel.cs | 4 +-
.../Attributes/CorrectFileTypeAttribute.cs | 59 +
.../Attributes/FileValidationAttribute.cs | 22 +
.../Attributes/MaxFileSizeAttribute.cs | 21 +-
.../CourseUnitType/CourseUnitType.cs | 9 +
.../DTO/CourseFilesTransferDTO.cs | 11 +
.../ContentService/DTO/FileInfoDTO.cs | 9 +-
.../ContentService/DTO/FileLinkDTO.cs | 11 +
.../ContentService/DTO/ProcessFilesDTO.cs | 17 +
.../ContentService/DTO/ScopeDTO.cs | 9 +
.../ContentService/DTO/ScopeMappingPairDTO.cs | 8 +
.../ViewModels/CourseViewModels.cs | 15 +-
.../ViewModels/CriterionViewModel.cs | 17 +
.../ViewModels/HomeworkTaskViewModels.cs | 11 +-
.../ViewModels/HomeworkViewModels.cs | 2 +-
.../ViewModels/QuestionModels.cs | 7 +
.../HwProj.Models/HwProj.Models.csproj | 7 +-
HwProj.Common/HwProj.Models/Roles/Roles.cs | 7 +-
.../SolutionsService/GetSolutionModel.cs | 2 +
.../SolutionsService/PostSolutionModel.cs | 14 -
.../SolutionsService/Solution.cs | 3 +-
.../CrudRepository.cs | 67 +
.../HwProj.Repositories.Net8.csproj | 11 +
.../ICrudRepository.cs | 21 +
.../HwProj.Repositories.Net8/IEntity.cs | 9 +
.../HwProj.Repositories/CrudRepository.cs | 2 +-
.../HwProj.Repositories.csproj | 2 +-
.../HwProj.Repositories/ReadOnlyRepository.cs | 2 +-
.../HwProj.Utils/Auth/AuthExtensions.cs | 26 -
.../Configuration/StartupExtensions.cs | 76 -
.../ExternalStorageConfiguration.cs | 10 +
.../LocalStorageConfiguration.cs | 6 +
.../Controllers/FilesController.cs | 124 +-
.../Controllers/SystemController.cs | 11 +
.../Extensions/AmazonS3Extensions.cs | 20 -
.../Extensions/ConfigurationExtensions.cs | 96 +-
.../Extensions/MappingExtensions.cs | 31 +
.../Extensions/WebApplicationExtensions.cs | 52 +-
.../HwProj.ContentService.API.csproj | 14 +-
...0511214729_AddFileRecordsTable.Designer.cs | 67 +
.../20250511214729_AddFileRecordsTable.cs | 45 +
...4851_AddFileToCourseUnitsTable.Designer.cs | 101 +
...0250511214851_AddFileToCourseUnitsTable.cs | 51 +
.../Migrations/ContentContextModelSnapshot.cs | 98 +
.../Models/DTO/FileTransferDTO.cs | 13 +
.../Models/DTO/UploadFileTaskDto.cs | 9 +
.../Models/DTO/UploadFileToS3Dto.cs | 8 +
.../Models/Database/ContentContext.cs | 22 +
.../Models/Database/FileRecord.cs | 16 +
.../Models/Database/FileToCourseUnit.cs | 13 +
.../Models/Enums/CourseUnitType.cs | 8 +
.../Models/Enums/FileStatus.cs | 10 +
.../Models/Messages/DeleteFileMessage.cs | 6 +
.../Models/Messages/FileDeletedMessage.cs | 5 +
.../Models/Messages/IProcessFileMessage.cs | 6 +
.../Models/Messages/ReDeleteFileMessage.cs | 5 +
.../Models/Messages/ReUploadFileMessage.cs | 5 +
.../Models/Messages/UpdateStatusMessage.cs | 9 +
.../Models/Messages/UploadFileMessage.cs | 9 +
.../HwProj.ContentService.API/Models/Scope.cs | 9 +
.../HwProj.ContentService.API/Program.cs | 29 +-
.../Repositories/FileRecordRepository.cs | 177 +
.../Repositories/IFileRecordRepository.cs | 24 +
.../Services/FileKeyService.cs | 62 +-
.../Services/FilesInfoService.cs | 87 +
.../Services/Interfaces/IFileKeyService.cs | 9 +
.../Services/Interfaces/IFilesInfoService.cs | 15 +
.../Services/Interfaces/ILocalFilesService.cs | 11 +
.../Services/Interfaces/IMessageProducer.cs | 14 +
.../Services/Interfaces/IRecoveryService.cs | 6 +
.../Services/Interfaces/IS3FilesService.cs | 13 +
.../Services/LocalFilesService.cs | 62 +
.../Services/MessageConsumer.cs | 339 +
.../Services/MessageProducer.cs | 92 +
.../Services/RecoveryService.cs | 62 +
.../Services/S3FilesService.cs | 193 +
.../appsettings.json | 13 +-
.../ContentServiceClient.cs | 135 +-
.../IContentServiceClient.cs | 15 +-
.../ApplicationProfile.cs | 2 +-
.../Controllers/CoursesController.cs | 30 +-
.../Controllers/HomeworksController.cs | 10 +-
.../Controllers/TasksController.cs | 97 +-
.../HwProj.CoursesService.API/Dockerfile | 2 +
.../Domains/MappingExtensions.cs | 49 +-
.../Domains/Validations.cs | 3 +-
.../HwProj.CoursesService.API.csproj | 2 +
.../20251230213439_Criteria.Designer.cs | 356 +
.../Migrations/20251230213439_Criteria.cs | 44 +
.../Migrations/CourseContextModelSnapshot.cs | 31 +-
.../Models/Course.cs | 6 +-
.../Models/CourseContext.cs | 3 +-
.../Models/Criterion.cs | 21 +
.../Models/HomeworkTask.cs | 5 +-
.../Repositories/CoursesRepository.cs | 15 +-
.../Repositories/HomeworksRepository.cs | 8 +-
.../Repositories/ICoursesRepository.cs | 2 +-
.../Repositories/IHomeworksRepository.cs | 2 +-
.../Repositories/ITaskQuestionsRepository.cs | 8 -
.../Repositories/ITasksRepository.cs | 9 +-
.../Repositories/TaskQuestionsRepository.cs | 13 +
.../Repositories/TasksRepository.cs | 79 +-
.../Services/CourseFilterUtils.cs | 1 -
.../Services/CoursesService.cs | 53 +-
.../Services/HomeworksService.cs | 22 +-
.../Services/ICoursesService.cs | 4 +-
.../Services/IHomeworksService.cs | 8 +-
.../Services/ITaskQuestionsService.cs | 16 +
.../Services/ITasksService.cs | 11 +-
.../Services/TaskQuestionsService.cs | 47 +
.../Services/TasksService.cs | 46 +-
.../HwProj.CoursesService.API/Startup.cs | 4 +
.../CoursesServiceClient.cs | 70 +-
.../ICoursesServiceClient.cs | 15 +-
.../CoursesServiceTests.cs | 86 +-
.../ConfigurationExtensions.cs | 59 +
.../HwProj.EventBus.Client.csproj | 4 +-
.../Implementations/EventBusRabbitMQ.cs | 8 +-
.../AuthService/AdminRegisterEvent.cs | 11 +
.../AuthService/InviteLecturerEvent.cs | 10 +
.../AuthService/PasswordRecoveryEvent.cs | 13 +
.../AuthService/RegisterEvent.cs | 22 +
.../AuthService/StudentRegisterEvent.cs | 11 +
.../LecturerAcceptToCourseEvent.cs | 12 +
.../LecturerInvitedToCourseEvent.cs | 12 +
.../LecturerRejectToCourseEvent.cs | 12 +
.../CoursesService/NewCourseMateEvent.cs | 13 +
.../CoursesService/NewHomeworkEvent.cs | 23 +
.../CoursesService/NewHomeworkTaskEvent.cs | 25 +
.../CoursesService/UpdateHomeworkEvent.cs | 20 +
.../UpdateSolutionMaxRatingEvent.cs | 18 +
.../UpdateTaskMaxRatingEvent.cs | 22 +
.../HwProj.NotificationService.Events.csproj | 12 +
.../SolutionsService/RateEvent.cs | 18 +
.../SolutionsService/StudentPassTaskEvent.cs | 23 +
.../AutomapperProfile.cs | 1 +
.../NotificationSettingsController.cs | 1 -
.../Dockerfile | 12 +-
.../InviteLecturerEventHandler.cs | 5 +-
.../LecturerAcceptToCourseEventHandler.cs | 4 +-
.../LecturerInvitedToCourseEventHandler.cs | 3 +-
.../LecturerRejectToCourseEventHandler.cs | 4 +-
.../EventHandlers/NewCourseMateHandler.cs | 1 -
.../EventHandlers/NewHomeworkEventHandler.cs | 4 +-
.../NewHomeworkTaskEventHandler.cs | 4 +-
.../PasswordRecoveryEventHandler.cs | 5 +-
.../EventHandlers/RateEventHandler.cs | 4 +-
.../EventHandlers/RegisterEventHandler.cs | 4 +-
.../StudentPassTaskEventHandler.cs | 4 +-
.../UpdateHomeworkEventHandler.cs | 4 +-
.../UpdateTaskMaxRatingEventHandler.cs | 4 +-
.../HwProj.NotificationsService.API.csproj | 25 +-
.../Models/Notification.cs | 21 +
.../Models/NotificationCategoryState.cs | 10 +
.../Models/NotificationsContext.cs | 3 +-
.../Repositories/INotificationsRepository.cs | 4 +-
.../Repositories/NotificationsRepository.cs | 3 +-
.../Services/EmailService.cs | 2 +-
.../Services/IEmailService.cs | 4 +-
.../Startup.cs | 38 +-
.../appsettings.json | 2 +-
.../HwProj.SolutionsService.API/Dockerfile | 1 +
.../HwProj.SolutionsService.API.csproj | 1 +
...50909235525_SolutionIsModified.Designer.cs | 74 +
.../20250909235525_SolutionIsModified.cs | 23 +
.../SolutionContextModelSnapshot.cs | 2 +
.../Services/SolutionsService.cs | 5 +-
.../HwProj.SolutionsService.API/Startup.cs | 1 +
.../SolutionsServiceTests.cs | 30 +-
.../IStudentsInfo/IStudentsInfo.csproj | 4 +
.../IStudentsInfo/IStudentsInformation.cs | 2 +-
.../StudentsInformationTests.cs | 4 +-
.../StudentsInfo/StudentsInformation.cs | 162 +-
HwProj.sln | 21 +
README.md | 47 +-
backups_builder.sh | 86 +
cleanup_old_backups.sh | 13 +
docker-compose.override.yml | 69 +-
docker-compose.yml | 21 +-
generate-swagger-client.ps1 | 6 +-
hwproj.front/.env | 2 +-
hwproj.front/Dockerfile | 6 +-
hwproj.front/eslint.config.js | 3 +-
hwproj.front/package-lock.json | 17567 +++++++++-------
hwproj.front/package.json | 14 +-
hwproj.front/src/App.tsx | 5 -
hwproj.front/src/api/ApiSingleton.ts | 15 +-
hwproj.front/src/api/CustomFilesApi.ts | 41 +-
hwproj.front/src/api/api.ts | 3462 +--
hwproj.front/src/components/Auth/Login.tsx | 69 +-
.../src/components/Auth/PasswordRecovery.tsx | 7 +-
hwproj.front/src/components/Auth/Register.tsx | 9 +-
.../src/components/Common/HomeworkTags.tsx | 2 +-
.../src/components/Common/MarkdownEditor.tsx | 62 +-
.../src/components/Common/MentorsList.tsx | 18 +-
.../src/components/Common/StudentTags.ts | 1 +
.../Styles/MarkdownEditorCommands.ru.tsx | 226 +
hwproj.front/src/components/Common/Tags.tsx | 39 +-
.../src/components/Common/UserAvatar.tsx | 17 +
.../src/components/Courses/AddCourseInfo.tsx | 96 +-
.../src/components/Courses/Course.tsx | 267 +-
.../components/Courses/CourseExperimental.tsx | 520 +-
.../src/components/Courses/CourseFilter.tsx | 121 +-
.../src/components/Courses/Courses.tsx | 21 +-
.../src/components/Courses/CoursesList.tsx | 42 +-
.../src/components/Courses/CreateCourse.tsx | 16 +-
.../components/Courses/ICreateCourseState.tsx | 6 +-
.../Courses/MentorWorkspaceModal.tsx | 108 +-
.../components/Courses/NewCourseStudents.tsx | 13 +-
.../Courses/Statistics/StudentStatsChart.tsx | 35 +-
.../Courses/Statistics/StudentsRadarChart.tsx | 55 +
.../src/components/Courses/StudentStats.tsx | 104 +-
.../Courses/Styles/StudentStatsCell.css | 24 +
hwproj.front/src/components/EditProfile.tsx | 21 +-
.../src/components/Experts/InviteModal.tsx | 10 +-
.../src/components/Experts/Notebook.tsx | 3 +-
.../src/components/Experts/RegisterModal.tsx | 91 +-
.../src/components/Files/CourseUnitType.ts | 5 +
.../src/components/Files/FilePreview.tsx | 276 +-
.../src/components/Files/FileStatus.ts | 8 +
.../src/components/Files/FilesHandler.ts | 85 +
.../src/components/Files/FilesPreviewList.tsx | 32 +-
.../src/components/Files/FilesUploadWaiter.ts | 181 +
.../src/components/Files/FilesUploader.tsx | 104 +-
.../src/components/Files/IFileInfo.ts | 8 +-
.../src/components/Files/IProcessFilesDto.ts | 9 +
.../Files/filesUploaderOverrides.css | 4 +
.../Homeworks/CourseHomeworkExperimental.tsx | 359 +-
.../Solutions/AddOrEditSolution.tsx | 106 +-
.../Solutions/StudentSolutionsPage.tsx | 118 +-
.../Solutions/TaskSolutionComponent.tsx | 1088 +-
.../components/Solutions/TaskSolutions.tsx | 55 +-
.../Solutions/TaskSolutionsPage.tsx | 39 +-
.../UnratedSolutionsAndOpenQuestions.tsx | 308 +
.../Students/StudentCharacteristics.tsx | 9 +-
.../Tasks/CourseTaskExperimental.tsx | 413 +-
.../src/components/Tasks/StudentStatsCell.tsx | 32 +-
.../src/components/Tasks/TaskDeadlines.tsx | 6 -
.../src/components/Tasks/TaskQuestions.tsx | 24 +-
.../src/components/Utils/ErrorsHandler.tsx | 8 +-
.../src/components/Utils/FileInfoConverter.ts | 23 +-
.../src/components/Utils/ProcessFilesUtils.ts | 20 +
hwproj.front/src/components/Workspace.tsx | 27 +-
hwproj.front/src/services/AuthService.ts | 10 +-
hwproj.front/src/services/Utils.ts | 8 +-
hwproj.front/vite.config.ts | 3 +-
restore_from_backup.sh | 70 +
send_to_yadisk.sh | 20 +
310 files changed, 22493 insertions(+), 12890 deletions(-)
create mode 100644 .github/workflows/build.yml
create mode 100644 HwProj.APIGateway/HwProj.APIGateway.API/Filters/FilesCountLimiter.cs
create mode 100644 HwProj.APIGateway/HwProj.APIGateway.API/Filters/FilesPrivacyFilter.cs
create mode 100644 HwProj.APIGateway/HwProj.APIGateway.API/Models/Solutions/PostSolutionModel.cs
create mode 100644 HwProj.AuthService/HwProj.AuthService.API/Extensions/MappingExtensions.cs
create mode 100644 HwProj.AuthService/HwProj.AuthService.API/Models/User.cs
create mode 100644 HwProj.Common/HwProj.Common.Net8/ConnectionString.cs
create mode 100644 HwProj.Common/HwProj.Common.Net8/HwProj.Common.Net8.csproj
create mode 100644 HwProj.Common/HwProj.Common.Net8/StartupExtensions.cs
create mode 100644 HwProj.Common/HwProj.Models/ContentService/Attributes/CorrectFileTypeAttribute.cs
create mode 100644 HwProj.Common/HwProj.Models/ContentService/Attributes/FileValidationAttribute.cs
create mode 100644 HwProj.Common/HwProj.Models/ContentService/CourseUnitType/CourseUnitType.cs
create mode 100644 HwProj.Common/HwProj.Models/ContentService/DTO/CourseFilesTransferDTO.cs
create mode 100644 HwProj.Common/HwProj.Models/ContentService/DTO/FileLinkDTO.cs
create mode 100644 HwProj.Common/HwProj.Models/ContentService/DTO/ProcessFilesDTO.cs
create mode 100644 HwProj.Common/HwProj.Models/ContentService/DTO/ScopeDTO.cs
create mode 100644 HwProj.Common/HwProj.Models/ContentService/DTO/ScopeMappingPairDTO.cs
create mode 100644 HwProj.Common/HwProj.Models/CoursesService/ViewModels/CriterionViewModel.cs
create mode 100644 HwProj.Common/HwProj.Repositories.Net8/CrudRepository.cs
create mode 100644 HwProj.Common/HwProj.Repositories.Net8/HwProj.Repositories.Net8.csproj
create mode 100644 HwProj.Common/HwProj.Repositories.Net8/ICrudRepository.cs
create mode 100644 HwProj.Common/HwProj.Repositories.Net8/IEntity.cs
create mode 100644 HwProj.ContentService/HwProj.ContentService.API/Configuration/ExternalStorageConfiguration.cs
create mode 100644 HwProj.ContentService/HwProj.ContentService.API/Configuration/LocalStorageConfiguration.cs
create mode 100644 HwProj.ContentService/HwProj.ContentService.API/Controllers/SystemController.cs
create mode 100644 HwProj.ContentService/HwProj.ContentService.API/Extensions/MappingExtensions.cs
create mode 100644 HwProj.ContentService/HwProj.ContentService.API/Migrations/20250511214729_AddFileRecordsTable.Designer.cs
create mode 100644 HwProj.ContentService/HwProj.ContentService.API/Migrations/20250511214729_AddFileRecordsTable.cs
create mode 100644 HwProj.ContentService/HwProj.ContentService.API/Migrations/20250511214851_AddFileToCourseUnitsTable.Designer.cs
create mode 100644 HwProj.ContentService/HwProj.ContentService.API/Migrations/20250511214851_AddFileToCourseUnitsTable.cs
create mode 100644 HwProj.ContentService/HwProj.ContentService.API/Migrations/ContentContextModelSnapshot.cs
create mode 100644 HwProj.ContentService/HwProj.ContentService.API/Models/DTO/FileTransferDTO.cs
create mode 100644 HwProj.ContentService/HwProj.ContentService.API/Models/DTO/UploadFileTaskDto.cs
create mode 100644 HwProj.ContentService/HwProj.ContentService.API/Models/DTO/UploadFileToS3Dto.cs
create mode 100644 HwProj.ContentService/HwProj.ContentService.API/Models/Database/ContentContext.cs
create mode 100644 HwProj.ContentService/HwProj.ContentService.API/Models/Database/FileRecord.cs
create mode 100644 HwProj.ContentService/HwProj.ContentService.API/Models/Database/FileToCourseUnit.cs
create mode 100644 HwProj.ContentService/HwProj.ContentService.API/Models/Enums/CourseUnitType.cs
create mode 100644 HwProj.ContentService/HwProj.ContentService.API/Models/Enums/FileStatus.cs
create mode 100644 HwProj.ContentService/HwProj.ContentService.API/Models/Messages/DeleteFileMessage.cs
create mode 100644 HwProj.ContentService/HwProj.ContentService.API/Models/Messages/FileDeletedMessage.cs
create mode 100644 HwProj.ContentService/HwProj.ContentService.API/Models/Messages/IProcessFileMessage.cs
create mode 100644 HwProj.ContentService/HwProj.ContentService.API/Models/Messages/ReDeleteFileMessage.cs
create mode 100644 HwProj.ContentService/HwProj.ContentService.API/Models/Messages/ReUploadFileMessage.cs
create mode 100644 HwProj.ContentService/HwProj.ContentService.API/Models/Messages/UpdateStatusMessage.cs
create mode 100644 HwProj.ContentService/HwProj.ContentService.API/Models/Messages/UploadFileMessage.cs
create mode 100644 HwProj.ContentService/HwProj.ContentService.API/Models/Scope.cs
create mode 100644 HwProj.ContentService/HwProj.ContentService.API/Repositories/FileRecordRepository.cs
create mode 100644 HwProj.ContentService/HwProj.ContentService.API/Repositories/IFileRecordRepository.cs
create mode 100644 HwProj.ContentService/HwProj.ContentService.API/Services/FilesInfoService.cs
create mode 100644 HwProj.ContentService/HwProj.ContentService.API/Services/Interfaces/IFileKeyService.cs
create mode 100644 HwProj.ContentService/HwProj.ContentService.API/Services/Interfaces/IFilesInfoService.cs
create mode 100644 HwProj.ContentService/HwProj.ContentService.API/Services/Interfaces/ILocalFilesService.cs
create mode 100644 HwProj.ContentService/HwProj.ContentService.API/Services/Interfaces/IMessageProducer.cs
create mode 100644 HwProj.ContentService/HwProj.ContentService.API/Services/Interfaces/IRecoveryService.cs
create mode 100644 HwProj.ContentService/HwProj.ContentService.API/Services/Interfaces/IS3FilesService.cs
create mode 100644 HwProj.ContentService/HwProj.ContentService.API/Services/LocalFilesService.cs
create mode 100644 HwProj.ContentService/HwProj.ContentService.API/Services/MessageConsumer.cs
create mode 100644 HwProj.ContentService/HwProj.ContentService.API/Services/MessageProducer.cs
create mode 100644 HwProj.ContentService/HwProj.ContentService.API/Services/RecoveryService.cs
create mode 100644 HwProj.ContentService/HwProj.ContentService.API/Services/S3FilesService.cs
create mode 100644 HwProj.CoursesService/HwProj.CoursesService.API/Migrations/20251230213439_Criteria.Designer.cs
create mode 100644 HwProj.CoursesService/HwProj.CoursesService.API/Migrations/20251230213439_Criteria.cs
create mode 100644 HwProj.CoursesService/HwProj.CoursesService.API/Models/Criterion.cs
create mode 100644 HwProj.CoursesService/HwProj.CoursesService.API/Repositories/TaskQuestionsRepository.cs
create mode 100644 HwProj.CoursesService/HwProj.CoursesService.API/Services/ITaskQuestionsService.cs
create mode 100644 HwProj.CoursesService/HwProj.CoursesService.API/Services/TaskQuestionsService.cs
create mode 100644 HwProj.EventBus/HwProj.EventBus.Client/ConfigurationExtensions.cs
create mode 100644 HwProj.NotificationsService/HwProj.NotificationService.Events/AuthService/AdminRegisterEvent.cs
create mode 100644 HwProj.NotificationsService/HwProj.NotificationService.Events/AuthService/InviteLecturerEvent.cs
create mode 100644 HwProj.NotificationsService/HwProj.NotificationService.Events/AuthService/PasswordRecoveryEvent.cs
create mode 100644 HwProj.NotificationsService/HwProj.NotificationService.Events/AuthService/RegisterEvent.cs
create mode 100644 HwProj.NotificationsService/HwProj.NotificationService.Events/AuthService/StudentRegisterEvent.cs
create mode 100644 HwProj.NotificationsService/HwProj.NotificationService.Events/CoursesService/LecturerAcceptToCourseEvent.cs
create mode 100644 HwProj.NotificationsService/HwProj.NotificationService.Events/CoursesService/LecturerInvitedToCourseEvent.cs
create mode 100644 HwProj.NotificationsService/HwProj.NotificationService.Events/CoursesService/LecturerRejectToCourseEvent.cs
create mode 100644 HwProj.NotificationsService/HwProj.NotificationService.Events/CoursesService/NewCourseMateEvent.cs
create mode 100644 HwProj.NotificationsService/HwProj.NotificationService.Events/CoursesService/NewHomeworkEvent.cs
create mode 100644 HwProj.NotificationsService/HwProj.NotificationService.Events/CoursesService/NewHomeworkTaskEvent.cs
create mode 100644 HwProj.NotificationsService/HwProj.NotificationService.Events/CoursesService/UpdateHomeworkEvent.cs
create mode 100644 HwProj.NotificationsService/HwProj.NotificationService.Events/CoursesService/UpdateSolutionMaxRatingEvent.cs
create mode 100644 HwProj.NotificationsService/HwProj.NotificationService.Events/CoursesService/UpdateTaskMaxRatingEvent.cs
create mode 100644 HwProj.NotificationsService/HwProj.NotificationService.Events/HwProj.NotificationService.Events.csproj
create mode 100644 HwProj.NotificationsService/HwProj.NotificationService.Events/SolutionsService/RateEvent.cs
create mode 100644 HwProj.NotificationsService/HwProj.NotificationService.Events/SolutionsService/StudentPassTaskEvent.cs
create mode 100644 HwProj.NotificationsService/HwProj.NotificationsService.API/Models/Notification.cs
create mode 100644 HwProj.NotificationsService/HwProj.NotificationsService.API/Models/NotificationCategoryState.cs
create mode 100644 HwProj.SolutionsService/HwProj.SolutionsService.API/Migrations/20250909235525_SolutionIsModified.Designer.cs
create mode 100644 HwProj.SolutionsService/HwProj.SolutionsService.API/Migrations/20250909235525_SolutionIsModified.cs
create mode 100644 backups_builder.sh
create mode 100644 cleanup_old_backups.sh
create mode 100644 hwproj.front/src/components/Common/StudentTags.ts
create mode 100644 hwproj.front/src/components/Common/Styles/MarkdownEditorCommands.ru.tsx
create mode 100644 hwproj.front/src/components/Common/UserAvatar.tsx
create mode 100644 hwproj.front/src/components/Courses/Statistics/StudentsRadarChart.tsx
create mode 100644 hwproj.front/src/components/Courses/Styles/StudentStatsCell.css
create mode 100644 hwproj.front/src/components/Files/CourseUnitType.ts
create mode 100644 hwproj.front/src/components/Files/FileStatus.ts
create mode 100644 hwproj.front/src/components/Files/FilesHandler.ts
create mode 100644 hwproj.front/src/components/Files/FilesUploadWaiter.ts
create mode 100644 hwproj.front/src/components/Files/IProcessFilesDto.ts
create mode 100644 hwproj.front/src/components/Files/filesUploaderOverrides.css
create mode 100644 hwproj.front/src/components/Solutions/UnratedSolutionsAndOpenQuestions.tsx
create mode 100644 hwproj.front/src/components/Utils/ProcessFilesUtils.ts
create mode 100644 restore_from_backup.sh
create mode 100644 send_to_yadisk.sh
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
new file mode 100644
index 000000000..2ea1b1972
--- /dev/null
+++ b/.github/workflows/build.yml
@@ -0,0 +1,28 @@
+name: Build (.NET 8)
+
+on:
+ push:
+ branches: [ master ]
+ pull_request:
+
+jobs:
+ build:
+ runs-on: windows-latest
+ env:
+ DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true
+ DOTNET_CLI_TELEMETRY_OPTOUT: true
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+
+ - name: Setup .NET 8
+ uses: actions/setup-dotnet@v4
+ with:
+ dotnet-version: '8.0.x'
+ cache: true
+ cache-dependency-path: |
+ **/*.csproj
+ **/*.sln
+
+ - name: Build
+ run: dotnet build HwProj.sln -c Release
diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml
index cd093db5a..ec90a1b8e 100644
--- a/.github/workflows/deploy.yml
+++ b/.github/workflows/deploy.yml
@@ -11,8 +11,10 @@ jobs:
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.SSH_HOST }}
+ port: ${{ secrets.SSH_PORT }}
username: ${{ secrets.SSH_USER }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
+ command_timeout: 30m
script: |
cd /home/${{ secrets.SSH_USER }}/docker/HwProj-2.0.1
echo "${{ secrets.SUDO_PASSWORD }}" | sudo -S ./update.sh
diff --git a/.gitignore b/.gitignore
index f67737477..7ed326a27 100644
--- a/.gitignore
+++ b/.gitignore
@@ -361,3 +361,4 @@ StyleCop.Cache
.env
swagger-codegen
hwproj.front/static_dist/
+hwproj.front/dist/
diff --git a/Directory.Build.props b/Directory.Build.props
index 733e4e7e8..12f6f9719 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -7,4 +7,4 @@
enable
-
\ No newline at end of file
+
diff --git a/HwProj.APIGateway/HwProj.APIGateway.API/ApplicationProfile.cs b/HwProj.APIGateway/HwProj.APIGateway.API/ApplicationProfile.cs
index 1df12835c..509d438c6 100644
--- a/HwProj.APIGateway/HwProj.APIGateway.API/ApplicationProfile.cs
+++ b/HwProj.APIGateway/HwProj.APIGateway.API/ApplicationProfile.cs
@@ -1,18 +1,14 @@
using AutoMapper;
-using HwProj.Models.AuthService.DTO;
using HwProj.Models.AuthService.ViewModels;
-using HwProj.Models.CoursesService;
using HwProj.Models.CoursesService.DTO;
-using HwProj.Models.CoursesService.ViewModels;
-namespace HwProj.APIGateway.API
+namespace HwProj.APIGateway.API;
+
+public class ApplicationProfile : Profile
{
- public class ApplicationProfile : Profile
+ public ApplicationProfile()
{
- public ApplicationProfile()
- {
- CreateMap();
- CreateMap();
- }
+ CreateMap();
+ CreateMap();
}
-}
\ No newline at end of file
+}
diff --git a/HwProj.APIGateway/HwProj.APIGateway.API/Controllers/AccountController.cs b/HwProj.APIGateway/HwProj.APIGateway.API/Controllers/AccountController.cs
index ddc60b9ce..cf6c21e1f 100644
--- a/HwProj.APIGateway/HwProj.APIGateway.API/Controllers/AccountController.cs
+++ b/HwProj.APIGateway/HwProj.APIGateway.API/Controllers/AccountController.cs
@@ -18,20 +18,12 @@ namespace HwProj.APIGateway.API.Controllers
{
[Route("api/[controller]")]
[ApiController]
- public class AccountController : AggregationController
+ public class AccountController(
+ IAuthServiceClient authClient,
+ ICoursesServiceClient coursesClient,
+ ISolutionsServiceClient solutionsServiceClient)
+ : AggregationController(authClient)
{
- private readonly ICoursesServiceClient _coursesClient;
- private readonly ISolutionsServiceClient _solutionsServiceClient;
-
- public AccountController(
- IAuthServiceClient authClient,
- ICoursesServiceClient coursesClient,
- ISolutionsServiceClient solutionsServiceClient) : base(authClient)
- {
- _coursesClient = coursesClient;
- _solutionsServiceClient = solutionsServiceClient;
- }
-
[HttpGet("getUserData/{userId}")]
[ProducesResponseType(typeof(AccountDataDto), (int)HttpStatusCode.OK)]
public async Task GetUserDataById(string userId)
@@ -52,7 +44,7 @@ public async Task GetUserData()
if (User.IsInRole(Roles.LecturerRole))
{
- var courses = await _coursesClient.GetAllUserCourses();
+ var courses = await coursesClient.GetAllUserCourses();
var courseEvents = courses
.Select(t => new CourseEvents
{
@@ -74,9 +66,9 @@ public async Task GetUserData()
}
var currentTime = DateTime.UtcNow;
- var taskDeadlines = await _coursesClient.GetTaskDeadlines();
+ var taskDeadlines = await coursesClient.GetTaskDeadlines();
var taskIds = taskDeadlines.Select(t => t.TaskId).ToArray();
- var solutions = await _solutionsServiceClient.GetLastTaskSolutions(taskIds, UserId);
+ var solutions = await solutionsServiceClient.GetLastTaskSolutions(taskIds, UserId);
var taskDeadlinesInfo = taskDeadlines
.Zip(solutions, (deadline, solution) => (deadline, solution))
.Where(t => currentTime <= t.deadline.DeadlineDate || t.solution == null)
@@ -160,7 +152,7 @@ public async Task ResetPassword(ResetPasswordViewModel model)
{
return await AuthServiceClient.ResetPassword(model);
}
-
+
[Authorize]
[HttpPost("github/url")]
[ProducesResponseType(typeof(UrlDto), (int)HttpStatusCode.OK)]
diff --git a/HwProj.APIGateway/HwProj.APIGateway.API/Controllers/AggregationController.cs b/HwProj.APIGateway/HwProj.APIGateway.API/Controllers/AggregationController.cs
index 2b852598c..0fc62e69b 100644
--- a/HwProj.APIGateway/HwProj.APIGateway.API/Controllers/AggregationController.cs
+++ b/HwProj.APIGateway/HwProj.APIGateway.API/Controllers/AggregationController.cs
@@ -5,39 +5,38 @@
using HwProj.Models.CoursesService.ViewModels;
using Microsoft.AspNetCore.Mvc;
-namespace HwProj.APIGateway.API.Controllers
+namespace HwProj.APIGateway.API.Controllers;
+
+public class AggregationController : ControllerBase
{
- public class AggregationController : ControllerBase
- {
- protected readonly IAuthServiceClient AuthServiceClient;
+ protected readonly IAuthServiceClient AuthServiceClient;
- protected AggregationController(IAuthServiceClient authServiceClient)
- {
- AuthServiceClient = authServiceClient;
- }
+ protected AggregationController(IAuthServiceClient authServiceClient)
+ {
+ AuthServiceClient = authServiceClient;
+ }
- protected string? UserId =>
- Request.HttpContext.User.Claims
- .FirstOrDefault(claim => claim.Type.ToString() == "_id")
- ?.Value;
+ protected string? UserId =>
+ Request.HttpContext.User.Claims
+ .FirstOrDefault(claim => claim.Type.ToString() == "_id")
+ ?.Value;
- protected async Task GetCoursePreviews(CoursePreview[] courses)
+ protected async Task GetCoursePreviews(CoursePreview[] courses)
+ {
+ var mentorIds = courses.SelectMany(t => t.MentorIds).Distinct().ToArray();
+ var mentors = await AuthServiceClient.GetAccountsData(mentorIds);
+ var mentorsDict = mentors.Where(x => x != null).ToDictionary(x => x.UserId);
+ return courses.Select(course => new CoursePreviewView
{
- var mentorIds = courses.SelectMany(t => t.MentorIds).Distinct().ToArray();
- var mentors = await AuthServiceClient.GetAccountsData(mentorIds);
- var mentorsDict = mentors.Where(x => x != null).ToDictionary(x => x.UserId);
- return courses.Select(course => new CoursePreviewView
- {
- Id = course.Id,
- Name = course.Name,
- GroupName = course.GroupName,
- IsCompleted = course.IsCompleted,
- Mentors = course.MentorIds
- .Select(x => mentorsDict.TryGetValue(x, out var mentor) ? mentor : null)
- .Where(x => x != null)
- .ToArray()!,
- TaskId = course.TaskId
- }).ToArray();
- }
+ Id = course.Id,
+ Name = course.Name,
+ GroupName = course.GroupName,
+ IsCompleted = course.IsCompleted,
+ Mentors = course.MentorIds
+ .Select(x => mentorsDict.TryGetValue(x, out var mentor) ? mentor : null)
+ .Where(x => x != null)
+ .ToArray()!,
+ TaskId = course.TaskId
+ }).ToArray();
}
}
diff --git a/HwProj.APIGateway/HwProj.APIGateway.API/Controllers/CourseGroupsController.cs b/HwProj.APIGateway/HwProj.APIGateway.API/Controllers/CourseGroupsController.cs
index 2ef9ea662..4d1bbd78f 100644
--- a/HwProj.APIGateway/HwProj.APIGateway.API/Controllers/CourseGroupsController.cs
+++ b/HwProj.APIGateway/HwProj.APIGateway.API/Controllers/CourseGroupsController.cs
@@ -7,97 +7,96 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
-namespace HwProj.APIGateway.API.Controllers
+namespace HwProj.APIGateway.API.Controllers;
+
+[Route("api/[controller]")]
+[ApiController]
+public class CourseGroupsController : AggregationController
{
- [Route("api/[controller]")]
- [ApiController]
- public class CourseGroupsController : AggregationController
- {
- private readonly ICoursesServiceClient _coursesClient;
+ private readonly ICoursesServiceClient _coursesClient;
- public CourseGroupsController(ICoursesServiceClient coursesClient) : base(null)
- {
- _coursesClient = coursesClient;
- }
+ public CourseGroupsController(ICoursesServiceClient coursesClient) : base(null)
+ {
+ _coursesClient = coursesClient;
+ }
- [HttpGet("{courseId}/getAll")]
- [ProducesResponseType(typeof(GroupViewModel[]), (int)HttpStatusCode.OK)]
- public async Task GetAllCourseGroups(long courseId)
- {
- var result = await _coursesClient.GetAllCourseGroups(courseId);
- return result == null
- ? NotFound() as IActionResult
- : Ok(result);
- }
+ [HttpGet("{courseId}/getAll")]
+ [ProducesResponseType(typeof(GroupViewModel[]), (int)HttpStatusCode.OK)]
+ public async Task GetAllCourseGroups(long courseId)
+ {
+ var result = await _coursesClient.GetAllCourseGroups(courseId);
+ return result == null
+ ? NotFound()
+ : Ok(result);
+ }
- [HttpPost("{courseId}/create")]
- [Authorize(Roles = Roles.LecturerRole)]
- [ProducesResponseType(typeof(long), (int)HttpStatusCode.OK)]
- public async Task CreateCourseGroup(CreateGroupViewModel model, long courseId)
- {
- var result = await _coursesClient.CreateCourseGroup(model, courseId);
- return Ok(result);
- }
+ [HttpPost("{courseId}/create")]
+ [Authorize(Roles = Roles.LecturerRole)]
+ [ProducesResponseType(typeof(long), (int)HttpStatusCode.OK)]
+ public async Task CreateCourseGroup(CreateGroupViewModel model, long courseId)
+ {
+ var result = await _coursesClient.CreateCourseGroup(model, courseId);
+ return Ok(result);
+ }
- [HttpDelete("{courseId}/delete/{groupId}")]
- [Authorize(Roles = Roles.LecturerRole)]
- public async Task DeleteCourseGroup(long courseId, long groupId)
- {
- await _coursesClient.DeleteCourseGroup(courseId, groupId);
- return Ok();
- }
+ [HttpDelete("{courseId}/delete/{groupId}")]
+ [Authorize(Roles = Roles.LecturerRole)]
+ public async Task DeleteCourseGroup(long courseId, long groupId)
+ {
+ await _coursesClient.DeleteCourseGroup(courseId, groupId);
+ return Ok();
+ }
- [HttpPost("{courseId}/update/{groupId}")]
- [Authorize(Roles = Roles.LecturerRole)]
- public async Task UpdateCourseGroup(UpdateGroupViewModel model, long courseId, long groupId)
- {
- await _coursesClient.UpdateCourseGroup(model, courseId, groupId);
- return Ok();
- }
+ [HttpPost("{courseId}/update/{groupId}")]
+ [Authorize(Roles = Roles.LecturerRole)]
+ public async Task UpdateCourseGroup(UpdateGroupViewModel model, long courseId, long groupId)
+ {
+ await _coursesClient.UpdateCourseGroup(model, courseId, groupId);
+ return Ok();
+ }
- [HttpGet("{courseId}/get")]
- [Authorize]
- [ProducesResponseType(typeof(GroupViewModel), (int)HttpStatusCode.OK)]
- public async Task GetCourseGroupsById(long courseId)
- {
- var result = await _coursesClient.GetCourseGroupsById(courseId, UserId);
- return result == null
- ? NotFound() as IActionResult
- : Ok(result);
- }
+ [HttpGet("{courseId}/get")]
+ [Authorize]
+ [ProducesResponseType(typeof(GroupViewModel), (int)HttpStatusCode.OK)]
+ public async Task GetCourseGroupsById(long courseId)
+ {
+ var result = await _coursesClient.GetCourseGroupsById(courseId, UserId);
+ return result == null
+ ? NotFound()
+ : Ok(result);
+ }
- [HttpPost("{courseId}/addStudentInGroup/{groupId}")]
- [Authorize(Roles = Roles.LecturerRole)]
- public async Task AddStudentInGroup(long courseId, long groupId, [FromQuery] string userId)
- {
- await _coursesClient.AddStudentInGroup(courseId, groupId, userId);
- return Ok();
- }
+ [HttpPost("{courseId}/addStudentInGroup/{groupId}")]
+ [Authorize(Roles = Roles.LecturerRole)]
+ public async Task AddStudentInGroup(long courseId, long groupId, [FromQuery] string userId)
+ {
+ await _coursesClient.AddStudentInGroup(courseId, groupId, userId);
+ return Ok();
+ }
- [HttpPost("{courseId}/removeStudentFromGroup/{groupId}")]
- [Authorize(Roles = Roles.LecturerRole)]
- public async Task RemoveStudentFromGroup(long courseId, long groupId, [FromQuery] string userId)
- {
- await _coursesClient.RemoveStudentFromGroup(courseId, groupId, userId);
- return Ok();
- }
+ [HttpPost("{courseId}/removeStudentFromGroup/{groupId}")]
+ [Authorize(Roles = Roles.LecturerRole)]
+ public async Task RemoveStudentFromGroup(long courseId, long groupId, [FromQuery] string userId)
+ {
+ await _coursesClient.RemoveStudentFromGroup(courseId, groupId, userId);
+ return Ok();
+ }
- [HttpGet("get/{groupId}")]
- [ProducesResponseType(typeof(GroupViewModel), (int)HttpStatusCode.OK)]
- public async Task GetGroup(long groupId)
- {
- var result = (await _coursesClient.GetGroupsById(groupId)).FirstOrDefault();
- return result == null
- ? NotFound() as IActionResult
- : Ok(result);
- }
+ [HttpGet("get/{groupId}")]
+ [ProducesResponseType(typeof(GroupViewModel), (int)HttpStatusCode.OK)]
+ public async Task GetGroup(long groupId)
+ {
+ var result = (await _coursesClient.GetGroupsById(groupId)).FirstOrDefault();
+ return result == null
+ ? NotFound()
+ : Ok(result);
+ }
- [HttpGet("getTasks/{groupId}")]
- [ProducesResponseType(typeof(long[]), (int)HttpStatusCode.OK)]
- public async Task GetGroupTasks(long groupId)
- {
- var result = await _coursesClient.GetGroupTasks(groupId);
- return Ok(result);
- }
+ [HttpGet("getTasks/{groupId}")]
+ [ProducesResponseType(typeof(long[]), (int)HttpStatusCode.OK)]
+ public async Task GetGroupTasks(long groupId)
+ {
+ var result = await _coursesClient.GetGroupTasks(groupId);
+ return Ok(result);
}
}
diff --git a/HwProj.APIGateway/HwProj.APIGateway.API/Controllers/CoursesController.cs b/HwProj.APIGateway/HwProj.APIGateway.API/Controllers/CoursesController.cs
index 8caac02bc..7ad6bf87a 100644
--- a/HwProj.APIGateway/HwProj.APIGateway.API/Controllers/CoursesController.cs
+++ b/HwProj.APIGateway/HwProj.APIGateway.API/Controllers/CoursesController.cs
@@ -11,292 +11,305 @@
using HwProj.Models.CoursesService.DTO;
using HwProj.Models.CoursesService.ViewModels;
using HwProj.Models.Roles;
+using IStudentsInfo;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
-using Microsoft.Extensions.Options;
-using IStudentsInfo;
-namespace HwProj.APIGateway.API.Controllers
+namespace HwProj.APIGateway.API.Controllers;
+
+[Route("api/[controller]")]
+[ApiController]
+public class CoursesController : AggregationController
{
- [Route("api/[controller]")]
- [ApiController]
- public class CoursesController : AggregationController
+ private readonly ICoursesServiceClient _coursesClient;
+ private readonly IMapper _mapper;
+ private readonly IStudentsInformationProvider _studentsInfo;
+
+ public CoursesController(
+ ICoursesServiceClient coursesClient,
+ IAuthServiceClient authServiceClient,
+ IMapper mapper,
+ IStudentsInformationProvider studentsInfo) : base(authServiceClient)
{
- private readonly ICoursesServiceClient _coursesClient;
- private readonly IMapper _mapper;
- private readonly IStudentsInformationProvider _studentsInfo;
-
- public CoursesController(
- ICoursesServiceClient coursesClient,
- IAuthServiceClient authServiceClient,
- IMapper mapper,
- IStudentsInformationProvider studentsInfo) : base(authServiceClient)
- {
- _coursesClient = coursesClient;
- _mapper = mapper;
- _studentsInfo = studentsInfo;
- }
+ _coursesClient = coursesClient;
+ _mapper = mapper;
+ _studentsInfo = studentsInfo;
+ }
- [HttpGet]
- [Authorize]
- public async Task GetAllCourses()
- {
- var courses = await _coursesClient.GetAllCourses();
- var result = await GetCoursePreviews(courses);
- return result;
- }
+ [HttpGet]
+ [Authorize]
+ public async Task GetAllCourses()
+ {
+ var courses = await _coursesClient.GetAllCourses();
+ var result = await GetCoursePreviews(courses);
+ return result;
+ }
+
+ [HttpGet("getAllData/{courseId}")]
+ [ProducesResponseType(typeof(CourseAllData), (int)HttpStatusCode.OK)]
+ public async Task GetAllCourseData(long courseId)
+ {
+ var courseResult = await _coursesClient.GetCourseDataRaw(courseId);
+ if (!courseResult.Succeeded)
+ return BadRequest(courseResult.Errors[0]);
- [HttpGet("getAllData/{courseId}")]
- [ProducesResponseType(typeof(CourseViewModel), (int)HttpStatusCode.OK)]
- public async Task GetAllCourseData(long courseId)
+ var assignedStudents = await _coursesClient.GetMentorsToAssignedStudents(courseId);
+ var result = new CourseAllData
{
- var courseResult = await _coursesClient.GetAllCourseData(courseId);
- if (!courseResult.Succeeded)
- return BadRequest(courseResult.Errors[0]);
+ Course = await ToCourseViewModel(courseResult.Value),
+ AssignedStudents = assignedStudents
+ };
+ return Ok(result);
+ }
- var result = await ToCourseViewModel(courseResult.Value);
- return Ok(result);
- }
+ [HttpGet("{courseId}")]
+ [ProducesResponseType(typeof(CourseViewModel), (int)HttpStatusCode.OK)]
+ public async Task GetCourseData(long courseId)
+ {
+ var course = await _coursesClient.GetCourseView(courseId);
+ if (course == null) return NotFound();
- [HttpGet("{courseId}")]
- [ProducesResponseType(typeof(CourseViewModel), (int)HttpStatusCode.OK)]
- public async Task GetCourseData(long courseId)
- {
- var course = await _coursesClient.GetCourseById(courseId);
- if (course == null) return NotFound();
+ var result = await ToCourseViewModel(course);
+ return Ok(result);
+ }
- var result = await ToCourseViewModel(course);
- return Ok(result);
- }
+ [HttpDelete("{courseId}")]
+ [Authorize(Roles = Roles.LecturerRole)]
+ public async Task DeleteCourse(long courseId)
+ {
+ await _coursesClient.DeleteCourse(courseId);
+ return Ok();
+ }
- [HttpDelete("{courseId}")]
- [Authorize(Roles = Roles.LecturerRole)]
- public async Task DeleteCourse(long courseId)
- {
- await _coursesClient.DeleteCourse(courseId);
- return Ok();
- }
+ [HttpGet("getGroups")]
+ [Authorize(Roles = Roles.LecturerRole)]
+ [ProducesResponseType(typeof(List), (int)HttpStatusCode.OK)]
+ public async Task GetGroups(string programName)
+ {
+ var groups = await _studentsInfo.GetGroups(programName);
+ return Ok(groups);
+ }
- [HttpGet("getGroups")]
- [Authorize(Roles = Roles.LecturerRole)]
- [ProducesResponseType(typeof(List), (int)HttpStatusCode.OK)]
- public async Task GetGroups(string programName)
- {
- var groups = await _studentsInfo.GetGroups(programName);
- return Ok(groups);
- }
+ [HttpGet("getProgramNames")]
+ [Authorize(Roles = Roles.LecturerRole)]
+ [ProducesResponseType(typeof(List), (int)HttpStatusCode.OK)]
+ public async Task GetProgramNames()
+ {
+ return Ok(await _studentsInfo.GetProgramNames());
+ }
- [HttpGet("getProgramNames")]
- [Authorize(Roles = Roles.LecturerRole)]
- [ProducesResponseType(typeof(List), (int)HttpStatusCode.OK)]
- public IActionResult GetProgramNames()
+ [HttpPost("create")]
+ [Authorize(Roles = Roles.LecturerRole)]
+ [ProducesResponseType(typeof(long), (int)HttpStatusCode.OK)]
+ public async Task CreateCourse(CreateCourseViewModel model)
+ {
+ if (model.GroupNames.Any() && model.FetchStudents)
{
- return Ok(_studentsInfo.GetProgramNames());
- }
+ var studentCandidates = new List();
- [HttpPost("create")]
- [Authorize(Roles = Roles.LecturerRole)]
- [ProducesResponseType(typeof(long), (int)HttpStatusCode.OK)]
- public async Task CreateCourse(CreateCourseViewModel model)
- {
- if (!string.IsNullOrEmpty(model.GroupName) && model.FetchStudents)
+ foreach (var groupName in model.GroupNames)
{
- var students = _studentsInfo.GetStudentInformation(model.GroupName);
- if (students.Count > 0)
- {
- var sortedStudents = students
- .Where(student => !string.IsNullOrEmpty(student.Email))
- .OrderBy(student => student.Surname)
- .ThenBy(student => student.Name)
- .ToList();
-
- var registrationModels = sortedStudents
- .Select(student => new RegisterViewModel
- {
- Email = student.Email,
- Name = student.Name,
- Surname = student.Surname,
- MiddleName = student.MiddleName
- }).ToList();
-
- var userIds = await AuthServiceClient.GetOrRegisterStudentsBatchAsync(registrationModels);
- model.StudentIDs = userIds.Where(x => x.Succeeded).Select(x => x.Value).ToList();
- }
+ var students = _studentsInfo.GetStudentInformation(groupName);
+ studentCandidates.AddRange(students);
}
- model.StudentIDs ??= new List();
- var result = await _coursesClient.CreateCourse(model);
- return result.Succeeded
- ? Ok(result.Value) as IActionResult
- : BadRequest(result.Errors);
+ var registrationModels = studentCandidates
+ .Where(student => !string.IsNullOrEmpty(student.Email))
+ .OrderBy(student => student.Surname)
+ .ThenBy(student => student.Name)
+ .Select(student => new RegisterViewModel
+ {
+ Email = student.Email,
+ Name = student.Name,
+ Surname = student.Surname,
+ MiddleName = student.MiddleName
+ })
+ .Distinct()
+ .ToList();
+
+ var userIds = await AuthServiceClient.GetOrRegisterStudentsBatchAsync(registrationModels);
+
+ var successfulIds = userIds
+ .Where(x => x.Succeeded)
+ .Select(x => x.Value)
+ .ToList();
+
+ model.StudentIDs = successfulIds;
}
- [HttpPost("update/{courseId}")]
- [Authorize(Roles = Roles.LecturerRole)]
- public async Task UpdateCourse(UpdateCourseViewModel model, long courseId)
- {
- await _coursesClient.UpdateCourse(model, courseId);
- return Ok();
- }
+ var result = await _coursesClient.CreateCourse(model);
+ return result.Succeeded
+ ? Ok(result.Value)
+ : BadRequest(result.Errors);
+ }
- [HttpPost("signInCourse/{courseId}")]
- [Authorize(Roles = Roles.StudentRole)]
- public async Task SignInCourse(long courseId)
- {
- await _coursesClient.SignInCourse(courseId, UserId);
- return Ok();
- }
+ [HttpPost("update/{courseId}")]
+ [Authorize(Roles = Roles.LecturerRole)]
+ public async Task UpdateCourse(UpdateCourseViewModel model, long courseId)
+ {
+ await _coursesClient.UpdateCourse(model, courseId);
+ return Ok();
+ }
- [HttpPost("acceptStudent/{courseId}/{studentId}")]
- [Authorize(Roles = Roles.LecturerRole)]
- public async Task AcceptStudent(long courseId, string studentId)
- {
- await _coursesClient.AcceptStudent(courseId, studentId);
- return Ok();
- }
+ [HttpPost("signInCourse/{courseId}")]
+ [Authorize(Roles = Roles.StudentRole)]
+ public async Task SignInCourse(long courseId)
+ {
+ await _coursesClient.SignInCourse(courseId, UserId);
+ return Ok();
+ }
- [HttpPost("rejectStudent/{courseId}/{studentId}")]
- [Authorize(Roles = Roles.LecturerRole)]
- public async Task RejectStudent(long courseId, string studentId)
- {
- await _coursesClient.RejectStudent(courseId, studentId);
- return Ok();
- }
+ [HttpPost("acceptStudent/{courseId}/{studentId}")]
+ [Authorize(Roles = Roles.LecturerRole)]
+ public async Task AcceptStudent(long courseId, string studentId)
+ {
+ await _coursesClient.AcceptStudent(courseId, studentId);
+ return Ok();
+ }
- [HttpPost("updateCharacteristics/{courseId}/{studentId}")]
- [Authorize(Roles = Roles.LecturerRole)]
- public async Task UpdateStudentCharacteristics(long courseId, string studentId,
- [FromBody] StudentCharacteristicsDto characteristics)
- {
- await _coursesClient.UpdateStudentCharacteristics(courseId, studentId, characteristics);
- return Ok();
- }
+ [HttpPost("rejectStudent/{courseId}/{studentId}")]
+ [Authorize(Roles = Roles.LecturerRole)]
+ public async Task RejectStudent(long courseId, string studentId)
+ {
+ await _coursesClient.RejectStudent(courseId, studentId);
+ return Ok();
+ }
- [HttpGet("userCourses")]
- [Authorize]
- public async Task GetAllUserCourses()
- {
- var userCourses = await _coursesClient.GetAllUserCourses();
- var result = await GetCoursePreviews(userCourses);
- return result;
- }
+ [HttpPost("updateCharacteristics/{courseId}/{studentId}")]
+ [Authorize(Roles = Roles.LecturerRole)]
+ public async Task UpdateStudentCharacteristics(long courseId, string studentId,
+ [FromBody] StudentCharacteristicsDto characteristics)
+ {
+ await _coursesClient.UpdateStudentCharacteristics(courseId, studentId, characteristics);
+ return Ok();
+ }
- [HttpGet("acceptLecturer/{courseId}/{lecturerEmail}")]
- [Authorize(Roles = Roles.LecturerRole)]
- public async Task AcceptLecturer(long courseId, string lecturerEmail)
- {
- var lecturer = await AuthServiceClient.GetAccountDataByEmail(lecturerEmail);
- if (lecturer == null) return NotFound("Преподаватель с такой почтой не найден");
- if (lecturer.Role != Roles.LecturerRole && lecturer.Role != Roles.ExpertRole)
- return BadRequest("Пользователь не является преподавателем");
-
- var result = await _coursesClient.AcceptLecturer(courseId, lecturerEmail, lecturer.UserId);
- return result.Succeeded
- ? Ok(result) as IActionResult
- : BadRequest(result.Errors);
- }
+ [HttpGet("userCourses")]
+ [Authorize]
+ public async Task GetAllUserCourses()
+ {
+ var userCourses = await _coursesClient.GetAllUserCourses();
+ var result = await GetCoursePreviews(userCourses);
+ return result;
+ }
- [HttpGet("getLecturersAvailableForCourse/{courseId}")]
- [Authorize(Roles = Roles.LecturerRole)]
- [ProducesResponseType(typeof(AccountDataDto[]), (int)HttpStatusCode.OK)]
- public async Task GetLecturersAvailableForCourse(long courseId)
- {
- var result = await _coursesClient.GetLecturersAvailableForCourse(courseId);
- return Ok(result.Value);
- }
+ [HttpGet("acceptLecturer/{courseId}/{lecturerEmail}")]
+ [Authorize(Roles = Roles.LecturerRole)]
+ public async Task AcceptLecturer(long courseId, string lecturerEmail)
+ {
+ var lecturer = await AuthServiceClient.GetAccountDataByEmail(lecturerEmail);
+ if (lecturer == null) return NotFound("Преподаватель с такой почтой не найден");
+ if (lecturer.Role != Roles.LecturerRole && lecturer.Role != Roles.ExpertRole)
+ return BadRequest("Пользователь не является преподавателем");
+
+ var result = await _coursesClient.AcceptLecturer(courseId, lecturerEmail, lecturer.UserId);
+ return result.Succeeded
+ ? Ok(result)
+ : BadRequest(result.Errors);
+ }
- [HttpGet("tags/{courseId}")]
- [Authorize(Roles = Roles.LecturerRole)]
- [ProducesResponseType(typeof(string[]), (int)HttpStatusCode.OK)]
- public async Task GetAllTagsForCourse(long courseId)
- {
- var result = await _coursesClient.GetAllTagsForCourse(courseId);
- return result.Succeeded
- ? Ok(result.Value) as IActionResult
- : BadRequest(result.Errors);
- }
+ [HttpGet("getLecturersAvailableForCourse/{courseId}")]
+ [Authorize(Roles = Roles.LecturerRole)]
+ [ProducesResponseType(typeof(AccountDataDto[]), (int)HttpStatusCode.OK)]
+ public async Task GetLecturersAvailableForCourse(long courseId)
+ {
+ var lecturers = await AuthServiceClient.GetAllLecturers();
+ var courseMentors = await _coursesClient.GetCourseLecturersIds(courseId);
+ var result = lecturers.Where(x => !courseMentors.Contains(x.UserId));
+ return Ok(result.ToArray());
+ }
- [HttpPost("editMentorWorkspace/{courseId}/{mentorId}")]
- [Authorize(Roles = Roles.LecturerRole)]
- public async Task EditMentorWorkspace(
- long courseId, string mentorId, EditMentorWorkspaceDTO editMentorWorkspaceDto)
- {
- var mentor = await AuthServiceClient.GetAccountData(mentorId);
- if (mentor == null)
- return NotFound("Пользователь с такой почтой не найден");
+ [HttpGet("tags/{courseId}")]
+ [Authorize(Roles = Roles.LecturerRole)]
+ [ProducesResponseType(typeof(string[]), (int)HttpStatusCode.OK)]
+ public async Task GetAllTagsForCourse(long courseId)
+ {
+ var result = await _coursesClient.GetAllTagsForCourse(courseId);
+ return result.Succeeded
+ ? Ok(result.Value)
+ : BadRequest(result.Errors);
+ }
- if (!Roles.LecturerOrExpertRole.Contains(mentor.Role))
- return BadRequest("Пользователь с такой почтой не является преподавателем или экспертом");
+ [HttpPost("editMentorWorkspace/{courseId}/{mentorId}")]
+ [Authorize(Roles = Roles.LecturerRole)]
+ public async Task EditMentorWorkspace(
+ long courseId, string mentorId, EditMentorWorkspaceDTO editMentorWorkspaceDto)
+ {
+ var mentor = await AuthServiceClient.GetAccountData(mentorId);
+ if (mentor == null)
+ return NotFound("Пользователь с такой почтой не найден");
- var courseFilterModel = _mapper.Map(editMentorWorkspaceDto);
- courseFilterModel.UserId = mentorId;
+ if (!Roles.LecturerOrExpertRole.Contains(mentor.Role))
+ return BadRequest("Пользователь с такой почтой не является преподавателем или экспертом");
- var courseFilterCreationResult =
- await _coursesClient.CreateOrUpdateCourseFilter(courseId, courseFilterModel);
+ var courseFilterModel = _mapper.Map(editMentorWorkspaceDto);
+ courseFilterModel.UserId = mentorId;
- return courseFilterCreationResult.Succeeded
- ? Ok() as IActionResult
- : BadRequest(courseFilterCreationResult.Errors[0]);
- }
+ var courseFilterCreationResult =
+ await _coursesClient.CreateOrUpdateCourseFilter(courseId, courseFilterModel);
- [HttpGet("getMentorWorkspace/{courseId}/{mentorId}")]
- [Authorize(Roles = Roles.LecturerRole)]
- [ProducesResponseType(typeof(WorkspaceViewModel), (int)HttpStatusCode.OK)]
- public async Task GetMentorWorkspace(long courseId, string mentorId)
- {
- var mentor = await AuthServiceClient.GetAccountData(mentorId);
- if (mentor == null)
- return NotFound("Пользователь с такой почтой не найден");
+ return courseFilterCreationResult.Succeeded
+ ? Ok()
+ : BadRequest(courseFilterCreationResult.Errors[0]);
+ }
- if (!Roles.LecturerOrExpertRole.Contains(mentor.Role))
- return BadRequest("Пользователь с такой почтой не является преподавателем или экспертом");
+ [HttpGet("getMentorWorkspace/{courseId}/{mentorId}")]
+ [Authorize(Roles = Roles.LecturerRole)]
+ [ProducesResponseType(typeof(WorkspaceViewModel), (int)HttpStatusCode.OK)]
+ public async Task GetMentorWorkspace(long courseId, string mentorId)
+ {
+ var mentor = await AuthServiceClient.GetAccountData(mentorId);
+ if (mentor == null)
+ return NotFound("Пользователь с такой почтой не найден");
- var mentorCourseView = await _coursesClient.GetCourseByIdForMentor(courseId, mentorId);
- if (!mentorCourseView.Succeeded)
- return BadRequest(mentorCourseView.Errors[0]);
+ if (!Roles.LecturerOrExpertRole.Contains(mentor.Role))
+ return BadRequest("Пользователь с такой почтой не является преподавателем или экспертом");
- var studentIds = mentorCourseView.Value.CourseMates.Select(t => t.StudentId).ToArray();
- var students = await AuthServiceClient.GetAccountsData(studentIds);
+ var mentorCourseView = await _coursesClient.GetCourseByIdForMentor(courseId, mentorId);
+ if (!mentorCourseView.Succeeded)
+ return BadRequest(mentorCourseView.Errors[0]);
- var workspace = new WorkspaceViewModel
- {
- Homeworks = mentorCourseView.Value.Homeworks,
- Students = students
- };
- return Ok(workspace);
- }
+ var studentIds = mentorCourseView.Value.CourseMates.Select(t => t.StudentId).ToArray();
+ var students = await AuthServiceClient.GetAccountsData(studentIds);
- private async Task ToCourseViewModel(CourseDTO course)
+ var workspace = new WorkspaceViewModel
{
- var studentIds = course.CourseMates.Select(t => t.StudentId).ToArray();
- var getStudentsTask = AuthServiceClient.GetAccountsData(studentIds);
- var getMentorsTask = AuthServiceClient.GetAccountsData(course.MentorIds);
+ Homeworks = mentorCourseView.Value.Homeworks,
+ Students = students.OrderBy(x => x.Surname).ThenBy(x => x.Name).ToArray()
+ };
+ return Ok(workspace);
+ }
- await Task.WhenAll(getStudentsTask, getMentorsTask);
+ private async Task ToCourseViewModel(CourseDTO course)
+ {
+ var studentIds = course.CourseMates.Select(t => t.StudentId).ToArray();
+ var getStudentsTask = AuthServiceClient.GetAccountsData(studentIds);
+ var getMentorsTask = AuthServiceClient.GetAccountsData(course.MentorIds);
- var students = getStudentsTask.Result;
+ await Task.WhenAll(getStudentsTask, getMentorsTask);
- var acceptedStudents = new List();
- var newStudents = new List();
- for (var i = 0; i < students.Length; i++)
- {
- if (!(students[i] is { } student)) continue;
- if (course.CourseMates[i].IsAccepted) acceptedStudents.Add(student);
- else newStudents.Add(student);
- }
+ var students = getStudentsTask.Result;
- return new CourseViewModel
- {
- Id = course.Id,
- Name = course.Name,
- GroupName = course.GroupName,
- Mentors = getMentorsTask.Result.Where(t => t != null).ToArray(),
- AcceptedStudents = acceptedStudents.ToArray(),
- NewStudents = newStudents.ToArray(),
- Homeworks = course.Homeworks,
- IsCompleted = course.IsCompleted,
- IsOpen = course.IsOpen,
- };
+ var acceptedStudents = new List();
+ var newStudents = new List();
+ for (var i = 0; i < students.Length; i++)
+ {
+ if (!(students[i] is { } student)) continue;
+ if (course.CourseMates[i].IsAccepted) acceptedStudents.Add(student);
+ else newStudents.Add(student);
}
+
+ return new CourseViewModel
+ {
+ Id = course.Id,
+ Name = course.Name,
+ GroupName = course.GroupName,
+ Mentors = getMentorsTask.Result.Where(t => t != null).ToArray(),
+ AcceptedStudents = acceptedStudents.ToArray(),
+ NewStudents = newStudents.ToArray(),
+ Homeworks = course.Homeworks,
+ IsCompleted = course.IsCompleted,
+ IsOpen = course.IsOpen
+ };
}
}
diff --git a/HwProj.APIGateway/HwProj.APIGateway.API/Controllers/ExpertsController.cs b/HwProj.APIGateway/HwProj.APIGateway.API/Controllers/ExpertsController.cs
index 9d5c39049..597159b8e 100644
--- a/HwProj.APIGateway/HwProj.APIGateway.API/Controllers/ExpertsController.cs
+++ b/HwProj.APIGateway/HwProj.APIGateway.API/Controllers/ExpertsController.cs
@@ -11,109 +11,108 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
-namespace HwProj.APIGateway.API.Controllers
+namespace HwProj.APIGateway.API.Controllers;
+
+[Route("api/[controller]")]
+[ApiController]
+public class ExpertsController : AggregationController
{
- [Route("api/[controller]")]
- [ApiController]
- public class ExpertsController : AggregationController
+ private readonly ICoursesServiceClient _coursesClient;
+ private readonly IMapper _mapper;
+
+ public ExpertsController(ICoursesServiceClient coursesClient,
+ IAuthServiceClient authServiceClient,
+ IMapper mapper) : base(authServiceClient)
+ {
+ _coursesClient = coursesClient;
+ _mapper = mapper;
+ }
+
+ [HttpPost("invite")]
+ [Authorize(Roles = Roles.LecturerRole)]
+ [ProducesResponseType(typeof(Result), (int)HttpStatusCode.OK)]
+ public async Task Invite(InviteExpertViewModel inviteExpertView)
+ {
+ var expert = await AuthServiceClient.GetAccountDataByEmail(inviteExpertView.UserEmail);
+ if (expert == null)
+ return NotFound("Эксперт с такой почтой не найден");
+
+ if (expert.Role != Roles.ExpertRole || inviteExpertView.UserId != expert.UserId)
+ return BadRequest("Пользователь с такой почтой не является экспертом");
+
+ if (inviteExpertView.UserId != expert.UserId)
+ return BadRequest("Идентификатор эксперта с такой почтой не соответствует переданному идентификатору");
+
+ var courseFilterDto = _mapper.Map(inviteExpertView);
+
+ var courseFilterCreationResult =
+ await _coursesClient.CreateOrUpdateCourseFilter(inviteExpertView.CourseId, courseFilterDto);
+ if (!courseFilterCreationResult.Succeeded) return BadRequest(courseFilterCreationResult.Errors);
+
+ var acceptanceResult = await _coursesClient.AcceptLecturer(inviteExpertView.CourseId,
+ inviteExpertView.UserEmail, inviteExpertView.UserId);
+ return Ok(acceptanceResult);
+ }
+
+ [HttpPost("register")]
+ [Authorize(Roles = Roles.LecturerRole)]
+ [ProducesResponseType(typeof(Result), (int)HttpStatusCode.OK)]
+ public async Task Register(RegisterExpertViewModel model)
+ {
+ var result = await AuthServiceClient.RegisterExpert(model, UserId);
+ return Ok(result);
+ }
+
+ [HttpPost("login")]
+ [ProducesResponseType(typeof(Result), (int)HttpStatusCode.OK)]
+ public async Task Login(TokenCredentials credentials)
+ {
+ var result = await AuthServiceClient.LoginExpert(credentials).ConfigureAwait(false);
+ return Ok(result);
+ }
+
+ [HttpGet("getToken")]
+ [Authorize(Roles = Roles.LecturerRole)]
+ [ProducesResponseType(typeof(Result), (int)HttpStatusCode.OK)]
+ public async Task GetToken(string expertEmail)
+ {
+ var tokenMeta = await AuthServiceClient.GetExpertToken(expertEmail);
+ return Ok(tokenMeta);
+ }
+
+ [HttpPost("setProfileIsEdited")]
+ [Authorize(Roles = Roles.ExpertRole)]
+ [ProducesResponseType(typeof(Result), (int)HttpStatusCode.OK)]
+ public async Task SetProfileIsEdited()
+ {
+ var result = await AuthServiceClient.SetExpertProfileIsEdited(UserId);
+ return Ok(result);
+ }
+
+ [HttpGet("isProfileEdited")]
+ [Authorize(Roles = Roles.ExpertRole)]
+ [ProducesResponseType(typeof(Result), (int)HttpStatusCode.OK)]
+ public async Task GetIsProfileEdited()
+ {
+ var result = await AuthServiceClient.GetIsExpertProfileEdited(UserId);
+ return Ok(result);
+ }
+
+ [HttpGet("getAll")]
+ [Authorize(Roles = Roles.LecturerRole)]
+ [ProducesResponseType(typeof(ExpertDataDTO[]), (int)HttpStatusCode.OK)]
+ public async Task GetAll()
+ {
+ var result = await AuthServiceClient.GetAllExperts();
+ return Ok(result);
+ }
+
+ [HttpPost("updateTags")]
+ [Authorize(Roles = Roles.LecturerRole)]
+ [ProducesResponseType(typeof(Result), (int)HttpStatusCode.OK)]
+ public async Task UpdateTags(UpdateExpertTagsDTO updateExpertTagsDto)
{
- private readonly ICoursesServiceClient _coursesClient;
- private readonly IMapper _mapper;
-
- public ExpertsController(ICoursesServiceClient coursesClient,
- IAuthServiceClient authServiceClient,
- IMapper mapper) : base(authServiceClient)
- {
- _coursesClient = coursesClient;
- _mapper = mapper;
- }
-
- [HttpPost("invite")]
- [Authorize(Roles = Roles.LecturerRole)]
- [ProducesResponseType(typeof(Result), (int)HttpStatusCode.OK)]
- public async Task Invite(InviteExpertViewModel inviteExpertView)
- {
- var expert = await AuthServiceClient.GetAccountDataByEmail(inviteExpertView.UserEmail);
- if (expert == null)
- return NotFound("Эксперт с такой почтой не найден");
-
- if (expert.Role != Roles.ExpertRole || inviteExpertView.UserId != expert.UserId)
- return BadRequest("Пользователь с такой почтой не является экспертом");
-
- if (inviteExpertView.UserId != expert.UserId)
- return BadRequest("Идентификатор эксперта с такой почтой не соответствует переданному идентификатору");
-
- var courseFilterDto = _mapper.Map(inviteExpertView);
-
- var courseFilterCreationResult =
- await _coursesClient.CreateOrUpdateCourseFilter(inviteExpertView.CourseId, courseFilterDto);
- if (!courseFilterCreationResult.Succeeded) return BadRequest(courseFilterCreationResult.Errors);
-
- var acceptanceResult = await _coursesClient.AcceptLecturer(inviteExpertView.CourseId,
- inviteExpertView.UserEmail, inviteExpertView.UserId);
- return Ok(acceptanceResult);
- }
-
- [HttpPost("register")]
- [Authorize(Roles = Roles.LecturerRole)]
- [ProducesResponseType(typeof(Result), (int)HttpStatusCode.OK)]
- public async Task Register(RegisterExpertViewModel model)
- {
- var result = await AuthServiceClient.RegisterExpert(model, UserId);
- return Ok(result);
- }
-
- [HttpPost("login")]
- [ProducesResponseType(typeof(Result), (int)HttpStatusCode.OK)]
- public async Task Login(TokenCredentials credentials)
- {
- var result = await AuthServiceClient.LoginExpert(credentials).ConfigureAwait(false);
- return Ok(result);
- }
-
- [HttpGet("getToken")]
- [Authorize(Roles = Roles.LecturerRole)]
- [ProducesResponseType(typeof(Result), (int)HttpStatusCode.OK)]
- public async Task GetToken(string expertEmail)
- {
- var tokenMeta = await AuthServiceClient.GetExpertToken(expertEmail);
- return Ok(tokenMeta);
- }
-
- [HttpPost("setProfileIsEdited")]
- [Authorize(Roles = Roles.ExpertRole)]
- [ProducesResponseType(typeof(Result), (int)HttpStatusCode.OK)]
- public async Task SetProfileIsEdited()
- {
- var result = await AuthServiceClient.SetExpertProfileIsEdited(UserId);
- return Ok(result);
- }
-
- [HttpGet("isProfileEdited")]
- [Authorize(Roles = Roles.ExpertRole)]
- [ProducesResponseType(typeof(Result), (int)HttpStatusCode.OK)]
- public async Task GetIsProfileEdited()
- {
- var result = await AuthServiceClient.GetIsExpertProfileEdited(UserId);
- return Ok(result);
- }
-
- [HttpGet("getAll")]
- [Authorize(Roles = Roles.LecturerRole)]
- [ProducesResponseType(typeof(ExpertDataDTO[]), (int)HttpStatusCode.OK)]
- public async Task GetAll()
- {
- var result = await AuthServiceClient.GetAllExperts();
- return Ok(result);
- }
-
- [HttpPost("updateTags")]
- [Authorize(Roles = Roles.LecturerRole)]
- [ProducesResponseType(typeof(Result), (int)HttpStatusCode.OK)]
- public async Task UpdateTags(UpdateExpertTagsDTO updateExpertTagsDto)
- {
- var result = await AuthServiceClient.UpdateExpertTags(UserId, updateExpertTagsDto);
- return Ok(result);
- }
+ var result = await AuthServiceClient.UpdateExpertTags(UserId, updateExpertTagsDto);
+ return Ok(result);
}
-}
\ No newline at end of file
+}
diff --git a/HwProj.APIGateway/HwProj.APIGateway.API/Controllers/FilesController.cs b/HwProj.APIGateway/HwProj.APIGateway.API/Controllers/FilesController.cs
index a47646ee7..6ee73f136 100644
--- a/HwProj.APIGateway/HwProj.APIGateway.API/Controllers/FilesController.cs
+++ b/HwProj.APIGateway/HwProj.APIGateway.API/Controllers/FilesController.cs
@@ -4,73 +4,88 @@
using HwProj.AuthService.Client;
using HwProj.ContentService.Client;
using HwProj.Models.ContentService.DTO;
-using HwProj.Models.Roles;
+using HwProj.Models.CourseUnitType;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
-namespace HwProj.APIGateway.API.Controllers
+namespace HwProj.APIGateway.API.Controllers;
+
+[Route("api/[controller]")]
+[Authorize]
+[ApiController]
+public class FilesController(
+ IAuthServiceClient authServiceClient,
+ IContentServiceClient contentServiceClient,
+ FilesPrivacyFilter privacyFilter,
+ FilesCountLimiter filesCountLimiter)
+ : AggregationController(authServiceClient)
{
- [Route("api/[controller]")]
- [Authorize]
- [ApiController]
- public class FilesController : AggregationController
+ [HttpPost("process")]
+ [ProducesResponseType((int)HttpStatusCode.OK)]
+ [ProducesResponseType((int)HttpStatusCode.Forbidden)]
+ [ProducesResponseType(typeof(string[]), (int)HttpStatusCode.BadRequest)]
+ public async Task Process([FromForm] ProcessFilesDTO processFilesDto)
{
- private readonly IContentServiceClient _contentServiceClient;
+ var checkRights = await privacyFilter.CheckUploadRights(UserId, processFilesDto.FilesScope);
+ if (!checkRights) return Forbid("Недостаточно прав для загрузки файлов");
- public FilesController(IAuthServiceClient authServiceClient,
- IContentServiceClient contentServiceClient) : base(authServiceClient)
- {
- _contentServiceClient = contentServiceClient;
- }
+ var checkCountLimit = await filesCountLimiter.CheckCountLimit(processFilesDto);
+ if (!checkCountLimit)
+ return Forbid("Слишком много файлов в решении." +
+ $"Максимальное количество файлов - ${FilesCountLimiter.MaxSolutionFiles}");
- [HttpPost("upload")]
- [Authorize(Roles = Roles.LecturerRole)]
- [ServiceFilter(typeof(CourseMentorOnlyAttribute))]
- [ProducesResponseType((int)HttpStatusCode.OK)]
- [ProducesResponseType(typeof(string[]), (int)HttpStatusCode.BadRequest)]
- [ProducesResponseType(typeof(string[]), (int)HttpStatusCode.ServiceUnavailable)]
- public async Task Upload([FromForm] UploadFileDTO uploadFileDto)
- {
- var result = await _contentServiceClient.UploadFileAsync(uploadFileDto);
- return result.Succeeded
- ? Ok() as IActionResult
- : StatusCode((int)HttpStatusCode.ServiceUnavailable, result.Errors);
- }
+ var result = await contentServiceClient.ProcessFilesAsync(processFilesDto);
+ return result.Succeeded
+ ? Ok()
+ : BadRequest(result.Errors);
+ }
- [HttpGet("downloadLink")]
- [ProducesResponseType(typeof(string), (int)HttpStatusCode.OK)]
- [ProducesResponseType(typeof(string[]), (int)HttpStatusCode.NotFound)]
- public async Task GetDownloadLink([FromQuery] string key)
- {
- var result = await _contentServiceClient.GetDownloadLinkAsync(key);
- return result.Succeeded
- ? Ok(result.Value) as IActionResult
- : NotFound(result.Errors);
- }
+ [HttpPost("statuses")]
+ [ProducesResponseType((int)HttpStatusCode.Forbidden)]
+ [ProducesResponseType(typeof(FileInfoDTO[]), (int)HttpStatusCode.OK)]
+ [ProducesResponseType(typeof(string[]), (int)HttpStatusCode.BadRequest)]
+ public async Task GetStatuses(ScopeDTO filesScope)
+ {
+ var checkRights = await privacyFilter.CheckUploadRights(UserId, filesScope);
+ if (!checkRights) return Forbid("Недостаточно прав для получения информации о файлах");
- [HttpGet("filesInfo/{courseId}")]
- [ProducesResponseType(typeof(FileInfoDTO[]), (int)HttpStatusCode.OK)]
- [ProducesResponseType(typeof(string[]), (int)HttpStatusCode.ServiceUnavailable)]
- public async Task GetFilesInfo(long courseId, [FromQuery] long? homeworkId = null)
- {
- var filesInfoResult = await _contentServiceClient.GetFilesInfo(courseId, homeworkId);
- return filesInfoResult.Succeeded
- ? Ok(filesInfoResult.Value) as IActionResult
- : StatusCode((int)HttpStatusCode.ServiceUnavailable, filesInfoResult.Errors);
- }
+ var result = await contentServiceClient.GetFilesStatuses(filesScope);
+ return result.Succeeded
+ ? Ok(result.Value)
+ : BadRequest(result.Errors);
+ }
+
+ [HttpGet("downloadLink")]
+ [ProducesResponseType((int)HttpStatusCode.Forbidden)]
+ [ProducesResponseType(typeof(string), (int)HttpStatusCode.OK)]
+ [ProducesResponseType(typeof(string[]), (int)HttpStatusCode.NotFound)]
+ public async Task GetDownloadLink([FromQuery] long fileId)
+ {
+ var linkDto = await contentServiceClient.GetDownloadLinkAsync(fileId);
+ if (!linkDto.Succeeded) return BadRequest(linkDto.Errors);
+
+ var result = linkDto.Value;
+ var userId = UserId;
- [HttpDelete]
- [Authorize(Roles = Roles.LecturerRole)]
- [ServiceFilter(typeof(CourseMentorOnlyAttribute))]
- [ProducesResponseType((int)HttpStatusCode.OK)]
- [ProducesResponseType(typeof(string[]), (int)HttpStatusCode.BadRequest)]
- [ProducesResponseType(typeof(string[]), (int)HttpStatusCode.NotFound)]
- public async Task DeleteFile([FromQuery] long courseId, [FromQuery] string key)
+ foreach (var scope in result.FileScopes)
{
- var deletionResult = await _contentServiceClient.DeleteFileAsync(key);
- return deletionResult.Succeeded
- ? Ok() as IActionResult
- : NotFound(deletionResult.Errors);
+ if (await privacyFilter.CheckDownloadRights(userId, scope))
+ return Ok(result.DownloadUrl);
}
+
+ return Forbid("Недостаточно прав для получения ссылки на файл");
+ }
+
+ [HttpGet("info/course/{courseId}")]
+ [ProducesResponseType(typeof(FileInfoDTO[]), (int)HttpStatusCode.OK)]
+ [ProducesResponseType(typeof(string[]), (int)HttpStatusCode.BadRequest)]
+ public async Task GetFilesInfo(long courseId,
+ [FromQuery] bool uploadedOnly = true,
+ [FromQuery] string courseUnitType = CourseUnitType.Homework)
+ {
+ var filesInfoResult = await contentServiceClient.GetFilesInfo(courseId, uploadedOnly, courseUnitType);
+ return filesInfoResult.Succeeded
+ ? Ok(filesInfoResult.Value)
+ : BadRequest(filesInfoResult.Errors);
}
-}
\ No newline at end of file
+}
diff --git a/HwProj.APIGateway/HwProj.APIGateway.API/Controllers/HomeworksController.cs b/HwProj.APIGateway/HwProj.APIGateway.API/Controllers/HomeworksController.cs
index 52eb39be0..6f871d362 100644
--- a/HwProj.APIGateway/HwProj.APIGateway.API/Controllers/HomeworksController.cs
+++ b/HwProj.APIGateway/HwProj.APIGateway.API/Controllers/HomeworksController.cs
@@ -7,63 +7,62 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
-namespace HwProj.APIGateway.API.Controllers
+namespace HwProj.APIGateway.API.Controllers;
+
+[Route("api/[controller]")]
+[ApiController]
+public class HomeworksController : ControllerBase
{
- [Route("api/[controller]")]
- [ApiController]
- public class HomeworksController : ControllerBase
- {
- private readonly ICoursesServiceClient _coursesClient;
+ private readonly ICoursesServiceClient _coursesClient;
- public HomeworksController(ICoursesServiceClient coursesClient)
- {
- _coursesClient = coursesClient;
- }
+ public HomeworksController(ICoursesServiceClient coursesClient)
+ {
+ _coursesClient = coursesClient;
+ }
- [HttpGet("get/{homeworkId}")]
- [Authorize]
- [ProducesResponseType(typeof(HomeworkViewModel), (int)HttpStatusCode.OK)]
- public async Task GetHomework(long homeworkId)
- {
- var result = await _coursesClient.GetHomework(homeworkId);
- return Ok(result);
- }
+ [HttpGet("get/{homeworkId}")]
+ [Authorize]
+ [ProducesResponseType(typeof(HomeworkViewModel), (int)HttpStatusCode.OK)]
+ public async Task GetHomework(long homeworkId)
+ {
+ var result = await _coursesClient.GetHomework(homeworkId);
+ return Ok(result);
+ }
- [HttpGet("getForEditing/{homeworkId}")]
- [Authorize(Roles = Roles.LecturerRole)]
- [ProducesResponseType(typeof(HomeworkViewModel), (int)HttpStatusCode.OK)]
- public async Task GetForEditingHomework(long homeworkId)
- {
- var result = await _coursesClient.GetForEditingHomework(homeworkId);
- return Ok(result);
- }
+ [HttpGet("getForEditing/{homeworkId}")]
+ [Authorize(Roles = Roles.LecturerRole)]
+ [ProducesResponseType(typeof(HomeworkViewModel), (int)HttpStatusCode.OK)]
+ public async Task GetForEditingHomework(long homeworkId)
+ {
+ var result = await _coursesClient.GetForEditingHomework(homeworkId);
+ return Ok(result);
+ }
- [HttpPost("{courseId}/add")]
- [Authorize(Roles = Roles.LecturerRole)]
- [ProducesResponseType(typeof(long), (int)HttpStatusCode.OK)]
- public async Task AddHomework(CreateHomeworkViewModel homeworkViewModel, long courseId)
- {
- var result = await _coursesClient.AddHomeworkToCourse(homeworkViewModel, courseId);
- return result.Succeeded
- ? Ok(result.Value) as IActionResult
- : BadRequest(result.Errors);
- }
+ [HttpPost("{courseId}/add")]
+ [Authorize(Roles = Roles.LecturerRole)]
+ [ProducesResponseType(typeof(Result), (int)HttpStatusCode.OK)]
+ public async Task AddHomework(CreateHomeworkViewModel homeworkViewModel, long courseId)
+ {
+ var result = await _coursesClient.AddHomeworkToCourse(homeworkViewModel, courseId);
+ return result.Succeeded
+ ? Ok(result)
+ : BadRequest(result);
+ }
- [HttpDelete("delete/{homeworkId}")]
- [Authorize(Roles = Roles.LecturerRole)]
- public async Task DeleteHomework(long homeworkId)
- {
- await _coursesClient.DeleteHomework(homeworkId);
- return Ok();
- }
+ [HttpDelete("delete/{homeworkId}")]
+ [Authorize(Roles = Roles.LecturerRole)]
+ public async Task DeleteHomework(long homeworkId)
+ {
+ await _coursesClient.DeleteHomework(homeworkId);
+ return Ok();
+ }
- [HttpPut("update/{homeworkId}")]
- [Authorize(Roles = Roles.LecturerRole)]
- [ProducesResponseType(typeof(Result), (int)HttpStatusCode.OK)]
- public async Task UpdateHomework(long homeworkId, CreateHomeworkViewModel homeworkViewModel)
- {
- var result = await _coursesClient.UpdateHomework(homeworkId, homeworkViewModel);
- return Ok(result);
- }
+ [HttpPut("update/{homeworkId}")]
+ [Authorize(Roles = Roles.LecturerRole)]
+ [ProducesResponseType(typeof(Result), (int)HttpStatusCode.OK)]
+ public async Task UpdateHomework(long homeworkId, CreateHomeworkViewModel homeworkViewModel)
+ {
+ var result = await _coursesClient.UpdateHomework(homeworkId, homeworkViewModel);
+ return Ok(result);
}
}
diff --git a/HwProj.APIGateway/HwProj.APIGateway.API/Controllers/NotificationsController.cs b/HwProj.APIGateway/HwProj.APIGateway.API/Controllers/NotificationsController.cs
index c8a3ef536..8a4dc0a89 100644
--- a/HwProj.APIGateway/HwProj.APIGateway.API/Controllers/NotificationsController.cs
+++ b/HwProj.APIGateway/HwProj.APIGateway.API/Controllers/NotificationsController.cs
@@ -1,66 +1,65 @@
using System;
-using HwProj.Models.NotificationsService;
-using HwProj.NotificationsService.Client;
-using Microsoft.AspNetCore.Mvc;
using System.Net;
using System.Threading.Tasks;
+using HwProj.Models.NotificationsService;
using HwProj.Models.Roles;
+using HwProj.NotificationsService.Client;
using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Mvc;
-namespace HwProj.APIGateway.API.Controllers
+namespace HwProj.APIGateway.API.Controllers;
+
+[Route("api/[controller]")]
+[Authorize]
+[ApiController]
+public class NotificationsController : AggregationController
{
- [Route("api/[controller]")]
- [Authorize]
- [ApiController]
- public class NotificationsController : AggregationController
- {
- private readonly INotificationsServiceClient _notificationsClient;
+ private readonly INotificationsServiceClient _notificationsClient;
- public NotificationsController(INotificationsServiceClient notificationsClient) : base(null)
- {
- _notificationsClient = notificationsClient;
- }
+ public NotificationsController(INotificationsServiceClient notificationsClient) : base(null)
+ {
+ _notificationsClient = notificationsClient;
+ }
- [HttpGet("getNewNotificationsCount")]
- public async Task GetNewNotificationsCount()
- {
- var count = await _notificationsClient.GetNewNotificationsCount(UserId);
- return count;
- }
+ [HttpGet("getNewNotificationsCount")]
+ public async Task GetNewNotificationsCount()
+ {
+ var count = await _notificationsClient.GetNewNotificationsCount(UserId);
+ return count;
+ }
- [HttpGet("get")]
- [ProducesResponseType(typeof(CategorizedNotifications[]), (int)HttpStatusCode.OK)]
- public async Task Get()
- {
- var result = await _notificationsClient.Get(UserId);
- return Ok(result);
- }
+ [HttpGet("get")]
+ [ProducesResponseType(typeof(CategorizedNotifications[]), (int)HttpStatusCode.OK)]
+ public async Task Get()
+ {
+ var result = await _notificationsClient.Get(UserId);
+ return Ok(result);
+ }
- [HttpPut("markAsSeen")]
- public async Task MarkAsSeen([FromBody] long[] notificationIds)
- {
- await _notificationsClient.MarkAsSeen(UserId, notificationIds);
- return Ok();
- }
+ [HttpPut("markAsSeen")]
+ public async Task MarkAsSeen([FromBody] long[] notificationIds)
+ {
+ await _notificationsClient.MarkAsSeen(UserId, notificationIds);
+ return Ok();
+ }
- [HttpGet("settings")]
- [Authorize]
- [ProducesResponseType(typeof(NotificationsSettingDto[]), (int)HttpStatusCode.OK)]
- public async Task GetSettings()
- {
- var isLecturer = User.IsInRole(Roles.LecturerRole);
- if (!isLecturer) return Ok(Array.Empty());
+ [HttpGet("settings")]
+ [Authorize]
+ [ProducesResponseType(typeof(NotificationsSettingDto[]), (int)HttpStatusCode.OK)]
+ public async Task GetSettings()
+ {
+ var isLecturer = User.IsInRole(Roles.LecturerRole);
+ if (!isLecturer) return Ok(Array.Empty());
- var settings = await _notificationsClient.GetSettings(UserId);
- return Ok(settings);
- }
+ var settings = await _notificationsClient.GetSettings(UserId);
+ return Ok(settings);
+ }
- [HttpPut("settings")]
- [Authorize]
- public async Task ChangeSetting(NotificationsSettingDto newSetting)
- {
- await _notificationsClient.ChangeSetting(UserId, newSetting);
- return Ok();
- }
+ [HttpPut("settings")]
+ [Authorize]
+ public async Task ChangeSetting(NotificationsSettingDto newSetting)
+ {
+ await _notificationsClient.ChangeSetting(UserId, newSetting);
+ return Ok();
}
}
diff --git a/HwProj.APIGateway/HwProj.APIGateway.API/Controllers/SolutionsController.cs b/HwProj.APIGateway/HwProj.APIGateway.API/Controllers/SolutionsController.cs
index cb1793305..1ab2fe869 100644
--- a/HwProj.APIGateway/HwProj.APIGateway.API/Controllers/SolutionsController.cs
+++ b/HwProj.APIGateway/HwProj.APIGateway.API/Controllers/SolutionsController.cs
@@ -4,10 +4,10 @@
using System.Net;
using System.Threading.Tasks;
using HwProj.APIGateway.API.ExceptionFilters;
-using HwProj.APIGateway.API.Extensions;
using HwProj.APIGateway.API.Models.Solutions;
using HwProj.AuthService.Client;
using HwProj.CoursesService.Client;
+using HwProj.Models.AuthService.DTO;
using HwProj.Models.CoursesService;
using HwProj.Models.CoursesService.DTO;
using HwProj.Models.CoursesService.ViewModels;
@@ -17,279 +17,299 @@
using HwProj.SolutionsService.Client;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
+using PostSolutionModel = HwProj.APIGateway.API.Models.Solutions.PostSolutionModel;
-namespace HwProj.APIGateway.API.Controllers
+namespace HwProj.APIGateway.API.Controllers;
+
+[Route("api/[controller]")]
+[ApiController]
+[ForbiddenExceptionFilter]
+public class SolutionsController : AggregationController
{
- [Route("api/[controller]")]
- [ApiController]
- [ForbiddenExceptionFilter]
- public class SolutionsController : AggregationController
- {
- private readonly ISolutionsServiceClient _solutionsClient;
- private readonly ICoursesServiceClient _coursesServiceClient;
+ private readonly ICoursesServiceClient _coursesServiceClient;
+ private readonly ISolutionsServiceClient _solutionsClient;
- public SolutionsController(ISolutionsServiceClient solutionsClient, IAuthServiceClient authServiceClient,
- ICoursesServiceClient coursesServiceClient) :
- base(authServiceClient)
- {
- _solutionsClient = solutionsClient;
- _coursesServiceClient = coursesServiceClient;
- }
+ public SolutionsController(ISolutionsServiceClient solutionsClient, IAuthServiceClient authServiceClient,
+ ICoursesServiceClient coursesServiceClient) :
+ base(authServiceClient)
+ {
+ _solutionsClient = solutionsClient;
+ _coursesServiceClient = coursesServiceClient;
+ }
- [HttpGet("{solutionId}")]
- [ProducesResponseType(typeof(Solution), (int)HttpStatusCode.OK)]
- public async Task GetSolutionById(long solutionId)
- {
- var result = await _solutionsClient.GetSolutionById(solutionId);
- return result == null
- ? NotFound() as IActionResult
- : Ok(result);
- }
+ [HttpGet("{solutionId}")]
+ [ProducesResponseType(typeof(Solution), (int)HttpStatusCode.OK)]
+ public async Task GetSolutionById(long solutionId)
+ {
+ var result = await _solutionsClient.GetSolutionById(solutionId);
+ return result == null
+ ? NotFound()
+ : Ok(result);
+ }
- [HttpGet("taskSolution/{taskId}/{studentId}")]
- [Authorize]
- [ProducesResponseType(typeof(UserTaskSolutionsPageData), (int)HttpStatusCode.OK)]
- public async Task GetStudentSolution(long taskId, string studentId)
+ [HttpGet("taskSolution/{taskId}/{studentId}")]
+ [Authorize]
+ [ProducesResponseType(typeof(UserTaskSolutionsPageData), (int)HttpStatusCode.OK)]
+ public async Task GetStudentSolution(long taskId, string studentId)
+ {
+ var course = await _coursesServiceClient.GetCourseByTask(taskId);
+ if (course == null) return NotFound();
+
+ var courseMate = course.AcceptedStudents.FirstOrDefault(t => t.StudentId == studentId);
+ if (courseMate == null) return NotFound();
+
+ var studentSolutions = (await _solutionsClient.GetCourseStatistics(course.Id, UserId))
+ .Single().Homeworks
+ .ToDictionary(x => x.Id);
+
+ var homeworks = course.Homeworks
+ .Where(t => t.Tasks.Any())
+ .GroupBy(GetGroupingKey)
+ .ToList();
+
+ var tasks = homeworks
+ .SelectMany(x => x.SelectMany(t => t.Tasks))
+ .ToDictionary(t => t.Id);
+
+ // Получаем группы только для выбранной задачи
+ var studentsOnCourse = course.AcceptedStudents
+ .Select(t => t.StudentId)
+ .ToArray();
+
+ var mentorIds = studentSolutions.Values
+ .SelectMany(t => t.Tasks)
+ .SelectMany(t => t.Solution)
+ .Select(t => t.LecturerId ?? "")
+ .Where(x => x != "")
+ .Distinct()
+ .ToArray();
+
+ var accounts = await AuthServiceClient.GetAccountsData(studentsOnCourse.Union(mentorIds).ToArray());
+
+ var solutionsGroupsIds = studentSolutions.Values
+ .SelectMany(t => t.Tasks)
+ .First(x => x.Id == taskId).Solution
+ .Select(s => s.GroupId)
+ .Distinct()
+ .ToList();
+
+ var accountsCache = accounts.ToDictionary(dto => dto.UserId);
+
+ var solutionsGroups = course.Groups
+ .Where(g => solutionsGroupsIds.Contains(g.Id))
+ .ToDictionary(t => t.Id);
+
+ var taskSolutions = homeworks.Select(group =>
{
- var course = await _coursesServiceClient.GetCourseByTask(taskId);
- if (course == null) return NotFound();
-
- var courseMate = course.AcceptedStudents.FirstOrDefault(t => t.StudentId == studentId);
- if (courseMate == null) return NotFound();
-
- var studentSolutions = (await _solutionsClient.GetCourseStatistics(course.Id, UserId))
- .Single().Homeworks
- .ToDictionary(x => x.Id);
-
- var homeworks = course.Homeworks
- .Where(t => t.Tasks.Any())
- .GroupBy(GetGroupingKey)
- .ToList();
-
- var tasks = homeworks
- .SelectMany(x => x.SelectMany(t => t.Tasks))
- .ToDictionary(t => t.Id);
-
- // Получаем группы только для выбранной задачи
- var studentsOnCourse = course.AcceptedStudents
- .Select(t => t.StudentId)
- .ToArray();
-
- var mentorIds = studentSolutions.Values
- .SelectMany(t => t.Tasks)
- .SelectMany(t => t.Solution)
- .Select(t => t.LecturerId ?? "")
- .Where(x => x != "")
- .Distinct()
- .ToArray();
-
- var accounts = await AuthServiceClient.GetAccountsData(studentsOnCourse.Union(mentorIds).ToArray());
-
- var solutionsGroupsIds = studentSolutions.Values
- .SelectMany(t => t.Tasks)
- .First(x => x.Id == taskId).Solution
- .Select(s => s.GroupId)
- .Distinct()
- .ToList();
-
- var accountsCache = accounts.ToDictionary(dto => dto.UserId);
-
- var solutionsGroups = course.Groups
- .Where(g => solutionsGroupsIds.Contains(g.Id))
- .ToDictionary(t => t.Id);
-
- var taskSolutions = homeworks.Select(group =>
+ var isSingle = group.Count() == 1;
+ return new HomeworksGroupUserTaskSolutions
{
- var isSingle = group.Count() == 1;
- return new HomeworksGroupUserTaskSolutions
- {
- GroupTitle = isSingle ? null : group.Key.id,
- HomeworkSolutions = group.Select(h =>
+ GroupTitle = isSingle ? null : group.Key.id,
+ HomeworkSolutions = group.Select(h =>
+ {
+ studentSolutions.TryGetValue(h.Id, out var solutions);
+ return new HomeworkUserTaskSolutions
{
- studentSolutions.TryGetValue(h.Id, out var solutions);
- return new HomeworkUserTaskSolutions
- {
- HomeworkTitle = h.Title,
- StudentSolutions = solutions!.Tasks.Select(t =>
+ HomeworkTitle = h.Title,
+ StudentSolutions = solutions!.Tasks.Select(t =>
+ {
+ var task = tasks[t.Id];
+ return new UserTaskSolutions2
{
- var task = tasks[t.Id];
- return new UserTaskSolutions2
- {
- MaxRating = task.MaxRating,
- Title = task.Title,
- Tags = task.Tags,
- TaskId = task.Id.ToString(),
- Solutions = t.Solution.Select(s => new GetSolutionModel(s,
- s.TaskId == taskId && s.GroupId is { } groupId
- ? solutionsGroups[groupId].StudentsIds
- .Select(x => accountsCache[x])
- .ToArray()
- : null,
- s.LecturerId == null ? null : accountsCache[s.LecturerId])).ToArray()
- };
- })
- .ToArray()
- };
- })
- .ToArray()
- };
- }).ToArray();
+ MaxRating = task.MaxRating,
+ Title = task.Title,
+ Tags = task.Tags,
+ TaskId = task.Id.ToString(),
+ Solutions = t.Solution.Select(s => new GetSolutionModel(s,
+ s.TaskId == taskId && s.GroupId is { } groupId
+ ? solutionsGroups[groupId].StudentsIds
+ .Select(x => accountsCache[x])
+ .ToArray()
+ : null,
+ s.LecturerId == null ? null : accountsCache[s.LecturerId])).ToArray()
+ };
+ })
+ .ToArray()
+ };
+ })
+ .ToArray()
+ };
+ }).ToArray();
- return Ok(new UserTaskSolutionsPageData()
- {
- CourseId = course.Id,
- CourseMates = accounts,
- TaskSolutions = taskSolutions
- });
- }
+ return Ok(new UserTaskSolutionsPageData
+ {
+ CourseId = course.Id,
+ CourseMates = accounts,
+ TaskSolutions = taskSolutions
+ });
+ }
- // Научить без конкретного taskId по courseId получать данные
- [Authorize]
- [HttpGet("tasks/{taskId}")]
- [ProducesResponseType(typeof(TaskSolutionStatisticsPageData), (int)HttpStatusCode.OK)]
- public async Task GetTaskSolutionsPageData(long taskId)
+ // Научить без конкретного taskId по courseId получать данные
+ [Authorize]
+ [HttpGet("tasks/{taskId}")]
+ [ProducesResponseType(typeof(TaskSolutionStatisticsPageData), (int)HttpStatusCode.OK)]
+ public async Task GetTaskSolutionsPageData(long taskId, string? secondMentorId = null)
+ {
+ var course = await _coursesServiceClient.GetCourseByTask(taskId);
+ //TODO: CourseMentorOnlyAttribute
+ if (course == null || !course.MentorIds.Contains(UserId)) return Forbid();
+
+ var students = course.AcceptedStudents.ToDictionary(x => x.StudentId);
+ var secondMentorStudentIds = new HashSet();
+ if (secondMentorId != null && course.MentorIds.Contains(secondMentorId))
{
- var course = await _coursesServiceClient.GetCourseByTask(taskId);
- //TODO: CourseMentorOnlyAttribute
- if (course == null || !course.MentorIds.Contains(UserId)) return Forbid();
+ var secondMentorCourseResult =
+ await _coursesServiceClient.GetCourseByIdForMentor(course.Id, secondMentorId);
+ if (!secondMentorCourseResult.Succeeded) return BadRequest(secondMentorCourseResult.Errors);
- var students = course.AcceptedStudents.ToDictionary(x => x.StudentId);
- var studentIds = students.Keys.ToArray();
+ foreach (var student in secondMentorCourseResult.Value.AcceptedStudents)
+ {
+ secondMentorStudentIds.Add(student.StudentId);
+ students.TryAdd(student.StudentId, student);
+ }
+ }
- var currentDateTime = DateTime.UtcNow;
- var actualHomeworks = course.Homeworks
- .Select(hw =>
- {
- hw.Tasks = hw.Tasks.Where(t => t.PublicationDate <= currentDateTime).ToList();
- return hw;
- })
- .Where(hw => hw.Tasks.Count > 0)
- .ToList();
-
- var homeworks = actualHomeworks
- .GroupBy(GetGroupingKey)
- .ToList();
-
- var homeworksGroup = homeworks
- .First(g => g.Any(h => h.Tasks.Any(t => t.Id == taskId)))
- .ToList();
-
- var homeworkIndex = homeworksGroup.FindIndex(t => t.Tasks.Any(x => x.Id == taskId));
- var taskIndex = homeworksGroup[homeworkIndex].Tasks.FindIndex(t => t.Id == taskId);
- var taskVersionIds = homeworksGroup.Select(h => h.Tasks[taskIndex].Id).ToArray();
-
- var taskIds = homeworks
- .SelectMany(x => x.SelectMany(t => t.Tasks))
- .Select(t => t.Id)
- .ToArray();
-
- var getUsersDataTask = AuthServiceClient.GetAccountsData(studentIds.Union(course.MentorIds).ToArray());
- var getStatisticsTasks =
- taskVersionIds.Select(x => _solutionsClient.GetTaskSolutionStatistics(course.Id, x)).ToList();
- var getStatsForTasks = _solutionsClient.GetTaskSolutionsStats(
- new GetTasksSolutionsModel
- {
- StudentIds = studentIds,
- TaskIds = taskIds
- });
+ var studentIds = students.Keys.ToArray();
- await Task.WhenAll(getUsersDataTask, Task.WhenAll(getStatisticsTasks), getStatsForTasks);
+ var currentDateTime = DateTime.UtcNow;
+ var actualHomeworks = course.Homeworks
+ .Select(hw =>
+ {
+ hw.Tasks = hw.Tasks.Where(t => t.PublicationDate <= currentDateTime).ToList();
+ return hw;
+ })
+ .Where(hw => hw.Tasks.Count > 0)
+ .ToList();
+
+ var homeworks = actualHomeworks
+ .GroupBy(GetGroupingKey)
+ .ToList();
+
+ var homeworksGroup = homeworks
+ .First(g => g.Any(h => h.Tasks.Any(t => t.Id == taskId)))
+ .ToList();
+
+ var homeworkIndex = homeworksGroup.FindIndex(t => t.Tasks.Any(x => x.Id == taskId));
+ var taskIndex = homeworksGroup[homeworkIndex].Tasks.FindIndex(t => t.Id == taskId);
+ var taskVersionIds = homeworksGroup.Select(h => h.Tasks[taskIndex].Id).ToArray();
+
+ var taskIds = homeworks
+ .SelectMany(x => x.SelectMany(t => t.Tasks))
+ .Select(t => t.Id)
+ .ToArray();
+
+ var getUsersDataTask = AuthServiceClient.GetAccountsData(studentIds.Union(course.MentorIds).ToArray());
+ var getStatisticsTasks =
+ taskVersionIds.Select(x => _solutionsClient.GetTaskSolutionStatistics(course.Id, x)).ToList();
+ var getStatsForTasks = _solutionsClient.GetTaskSolutionsStats(
+ new GetTasksSolutionsModel
+ {
+ StudentIds = studentIds,
+ TaskIds = taskIds
+ });
+
+ await Task.WhenAll(getUsersDataTask, Task.WhenAll(getStatisticsTasks), getStatsForTasks);
- var usersData = getUsersDataTask.Result.ToDictionary(t => t.UserId);
- var statistics = taskVersionIds
- .Zip(getStatisticsTasks,
- (id, solutions) => (id, statistic: solutions.Result.ToDictionary(t => t.StudentId)))
- .ToDictionary(tuple => tuple.id, tuple => tuple.statistic);
+ var usersData = getUsersDataTask.Result.ToDictionary(t => t.UserId);
+ var statistics = taskVersionIds
+ .Zip(getStatisticsTasks,
+ (id, solutions) => (id, statistic: solutions.Result.ToDictionary(t => t.StudentId)))
+ .ToDictionary(tuple => tuple.id, tuple => tuple.statistic);
- var statsForTasks = getStatsForTasks.Result.ToDictionary(t => t.TaskId);
- var groups = course.Groups.ToDictionary(
- t => t.Id,
- t => t.StudentsIds.Select(s => usersData[s]).ToArray());
+ var statsForTasks = getStatsForTasks.Result.ToDictionary(t => t.TaskId);
+ var groups = course.Groups.ToDictionary(
+ t => t.Id,
+ t => t.StudentsIds.Select(s => usersData[s]).ToArray());
- var result = new TaskSolutionStatisticsPageData()
+ var result = new TaskSolutionStatisticsPageData
+ {
+ CourseId = course.Id,
+ TaskSolutions = taskVersionIds.Select(tId =>
{
- CourseId = course.Id,
- TaskSolutions = taskVersionIds.Select(tId =>
+ statistics.TryGetValue(tId, out var statistic);
+ return new TaskSolutions
{
- statistics.TryGetValue(tId, out var statistic);
- return new TaskSolutions
- {
- TaskId = tId,
- StudentSolutions = studentIds.Select(studentId => new UserTaskSolutions
+ TaskId = tId,
+ StudentSolutions = studentIds.Select(studentId => new UserTaskSolutions
+ {
+ Solutions = statistic!.TryGetValue(studentId, out var studentSolutions)
+ ? studentSolutions.Solutions.Select(t => new GetSolutionModel(t,
+ t.GroupId is { } groupId ? groups[groupId] : null,
+ t.LecturerId == null ? null : usersData[t.LecturerId])).ToArray()
+ : Array.Empty(),
+ Student = new StudentDataDto(usersData[studentId])
{
- Solutions = statistic!.TryGetValue(studentId, out var studentSolutions)
- ? studentSolutions.Solutions.Select(t => new GetSolutionModel(t,
- t.GroupId is { } groupId ? groups[groupId] : null,
- t.LecturerId == null ? null : usersData[t.LecturerId])).ToArray()
- : Array.Empty(),
- Student = new StudentDataDto(usersData[studentId])
- {
- Characteristics = students[studentId].Characteristics,
- }
- })
- .OrderBy(t => t.Student.Surname)
- .ThenBy(t => t.Student.Name)
- .ToArray(),
- };
- }).ToArray(),
- StatsForTasks = homeworks.Select(group =>
+ Characteristics = students[studentId].Characteristics
+ },
+ HasDifferentReviewer = secondMentorStudentIds.Contains(studentId)
+ })
+ .OrderBy(x => !x.HasDifferentReviewer)
+ .ThenBy(t => t.Student.Surname)
+ .ThenBy(t => t.Student.Name)
+ .ToArray()
+ };
+ }).ToArray(),
+ StatsForTasks = homeworks.Select(group =>
+ {
+ var isSingle = homeworks.Count == 1;
+ return new HomeworksGroupSolutionStats
{
- var isSingle = homeworks.Count == 1;
- return new HomeworksGroupSolutionStats()
+ GroupTitle = isSingle ? null : group.Key.id,
+ StatsForHomeworks = group.Select(h => new HomeworkSolutionsStats
{
- GroupTitle = isSingle ? null : group.Key.id,
- StatsForHomeworks = group.Select(h => new HomeworkSolutionsStats
+ HomeworkTitle = h.Title,
+ StatsForTasks = h.Tasks.Select(t =>
{
- HomeworkTitle = h.Title,
- StatsForTasks = h.Tasks.Select(t =>
- {
- var stats = statsForTasks.TryGetValue(t.Id, out var taskStats)
- ? taskStats
- : new TaskSolutionsStats();
-
- stats.Title = t.Title;
- stats.Tags = t.Tags;
- return stats;
- }).ToArray()
+ var stats = statsForTasks.TryGetValue(t.Id, out var taskStats)
+ ? taskStats
+ : new TaskSolutionsStats();
+
+ stats.Title = t.Title;
+ stats.Tags = t.Tags;
+ return stats;
}).ToArray()
- };
- }).ToArray()
- };
+ }).ToArray()
+ };
+ }).ToArray(),
- return Ok(result);
- }
+ CourseMentors = course.MentorIds.Select(t => usersData[t]).ToArray()
+ };
- [HttpPost("{taskId}")]
- [Authorize(Roles = Roles.StudentRole)]
- [ProducesResponseType(typeof(long), (int)HttpStatusCode.OK)]
- public async Task PostSolution(SolutionViewModel model, long taskId)
- {
- var solutionModel = new PostSolutionModel(model)
- {
- StudentId = UserId
- };
+ return Ok(result);
+ }
- var course = await _coursesServiceClient.GetCourseByTask(taskId);
- if (course is null) return BadRequest();
+ [HttpPost("{taskId}")]
+ [Authorize(Roles = Roles.StudentRole)]
+ [ProducesResponseType(typeof(long), (int)HttpStatusCode.OK)]
+ public async Task PostSolution(PostSolutionModel model, long taskId)
+ {
+ var solutionModel = new HwProj.Models.SolutionsService.PostSolutionModel
+ {
+ GithubUrl = model.GithubUrl,
+ Comment = model.Comment,
+ StudentId = UserId
+ };
- var courseMate = course.AcceptedStudents.FirstOrDefault(t => t.StudentId == solutionModel.StudentId);
- if (courseMate == null) return BadRequest($"Студента с id {solutionModel.StudentId} не существует");
+ var course = await _coursesServiceClient.GetCourseByTask(taskId);
+ if (course is null) return BadRequest();
- if (model.GroupMateIds == null || model.GroupMateIds.Length == 0)
- {
- var result = await _solutionsClient.PostSolution(taskId, solutionModel);
- return Ok(result);
- }
+ var courseMate = course.AcceptedStudents.FirstOrDefault(t => t.StudentId == solutionModel.StudentId);
+ if (courseMate == null) return BadRequest($"Студента с id {solutionModel.StudentId} не существует");
+ if (model.GroupMateIds == null || model.GroupMateIds.Length == 0)
+ {
+ var result = await _solutionsClient.PostSolution(taskId, solutionModel);
+ return Ok(result);
+ }
+ else
+ {
var fullStudentsGroup = model.GroupMateIds.ToList();
fullStudentsGroup.Add(solutionModel.StudentId);
var arrFullStudentsGroup = fullStudentsGroup.Distinct().ToArray();
if (arrFullStudentsGroup.Intersect(course.CourseMates.Select(x => x.StudentId)).Count() !=
arrFullStudentsGroup.Length)
- {
return BadRequest();
- }
var existedGroup = course.Groups.SingleOrDefault(x =>
x.StudentsIds.Length == arrFullStudentsGroup.Length &&
@@ -300,206 +320,266 @@ public async Task PostSolution(SolutionViewModel model, long task
await _coursesServiceClient.CreateCourseGroup(new CreateGroupViewModel(arrFullStudentsGroup, course.Id),
taskId);
- await _solutionsClient.PostSolution(taskId, solutionModel);
+ var result = await _solutionsClient.PostSolution(taskId, solutionModel);
- return Ok(solutionModel);
+ return Ok(result);
}
+ }
- [HttpPost("rateEmptySolution/{taskId}")]
- [Authorize(Roles = Roles.LecturerOrExpertRole)]
- public async Task PostEmptySolutionWithRate(long taskId, SolutionViewModel solution)
- {
- var course = await _coursesServiceClient.GetCourseByTask(taskId);
- if (course == null || !course.MentorIds.Contains(UserId)) return Forbid();
- if (course.CourseMates.All(t => t.StudentId != solution.StudentId))
- return BadRequest($"Студент с id {solution.StudentId} не записан на курс");
-
- solution.Comment = "[Решение было сдано вне сервиса]";
- await _solutionsClient.PostEmptySolutionWithRate(taskId, solution);
- return Ok();
- }
+ [HttpPost("automated/{courseId}")]
+ [Authorize(Roles = Roles.LecturerOrExpertRole)]
+ [ProducesResponseType(typeof(void), (int)HttpStatusCode.OK)]
+ public async Task PostAutomatedSolution(PostAutomatedSolutionModel model, long courseId)
+ {
+ var course = await _coursesServiceClient.GetCourseById(courseId);
+ if (course is null) return BadRequest($"Курс с Id {courseId} не найден");
+ if (!course.MentorIds.Contains(UserId))
+ return BadRequest("Добавлять автоматизированные решения могут только зарегистрированные на курс агенты");
- [HttpPost("giveUp/{taskId}")]
- [Authorize(Roles = Roles.StudentRole)]
- public async Task GiveUp(long taskId)
+ var tasks = course.Homeworks.SelectMany(t => t.Tasks);
+ var task = model.TaskIdType switch
{
- var course = await _coursesServiceClient.GetCourseByTask(taskId);
- if (course == null) return NotFound();
- if (course.CourseMates.All(t => t.StudentId != UserId))
- return BadRequest($"Студент с id {UserId} не записан на курс");
-
- await _solutionsClient.PostEmptySolutionWithRate(taskId, new SolutionViewModel()
- {
- StudentId = UserId,
- Comment = "[Студент отказался от выполнения задачи]",
- Rating = 0
- });
- return Ok();
- }
-
- [HttpPost("rateSolution/{solutionId}")]
- [Authorize(Roles = Roles.LecturerOrExpertRole)]
- public async Task RateSolution(long solutionId,
- RateSolutionModel rateSolutionModel)
+ TaskIdType.Id when long.TryParse(model.TaskId, out var taskId) => tasks.FirstOrDefault(x => x.Id == taskId),
+ TaskIdType.Title => tasks.FirstOrDefault(x => x.Title == model.TaskId),
+ _ => null
+ };
+ if (task is null) return BadRequest($"Задача с {model.TaskIdType} = {model.TaskId} не найдена");
+
+ var students =
+ await AuthServiceClient.GetAccountsData(course.AcceptedStudents.Select(x => x.StudentId).ToArray());
+ var studentCandidates = model.StudentIdType switch
{
- await _solutionsClient.RateSolution(solutionId, rateSolutionModel);
- return Ok();
- }
-
- [HttpGet("actuality/{solutionId}")]
- [ProducesResponseType(typeof(SolutionActualityDto), (int)HttpStatusCode.OK)]
- public async Task GetSolutionActuality(long solutionId)
+ StudentIdType.Id => students.FirstOrDefault(x => x.UserId == model.StudentId) is { } s
+ ? [s]
+ : [],
+ StudentIdType.FullName => students
+ .Where(x =>
+ model.StudentId.Contains(x.Name.Trim()) &&
+ model.StudentId.Contains(x.Surname.Trim()) &&
+ (string.IsNullOrEmpty(x.MiddleName) || model.StudentId.Contains(x.MiddleName.Trim())))
+ .ToArray(),
+ StudentIdType.GitHub => students.Where(x => x.GithubId == model.StudentId).ToArray(),
+ _ => []
+ };
+
+ switch (studentCandidates.Length)
{
- var result = await _solutionsClient.GetSolutionActuality(solutionId);
- return Ok(result);
+ case 0:
+ return BadRequest($"Студент с {model.StudentIdType} = {model.StudentId} не записан на курс");
+ case > 1:
+ return BadRequest(
+ $"Найдено несколько студентов с {model.StudentIdType} = {model.StudentId}. Измените StudentIdType или StudentId, чтобы уточнить запрос");
}
- [HttpPost("markSolutionFinal/{solutionId}")]
- [Authorize(Roles = Roles.LecturerRole)]
- public async Task MarkSolution(long solutionId)
- {
- await _solutionsClient.MarkSolution(solutionId);
- return Ok();
- }
+ var student = studentCandidates.First();
+ var solutions = await _solutionsClient.GetUserSolutions(task.Id, student.UserId);
+ if (solutions.OrderBy(x => x.PublicationDate).LastOrDefault()?.State == SolutionState.Posted)
+ return Ok(
+ "Последнее решение студента по задаче ещё не проверено. Все хорошо, но новое решение не будет добавлено");
- [HttpDelete("delete/{solutionId}")]
- [Authorize(Roles = Roles.LecturerRole)]
- public async Task DeleteSolution(long solutionId)
+ await _solutionsClient.PostSolution(task.Id, new HwProj.Models.SolutionsService.PostSolutionModel
{
- await _solutionsClient.DeleteSolution(solutionId);
- return Ok();
- }
+ GithubUrl = model.GithubUrl,
+ Comment = model.Comment,
+ StudentId = student.UserId
+ });
- [HttpGet("unratedSolutions")]
- [Authorize(Roles = Roles.LecturerOrExpertRole)]
- public async Task GetUnratedSolutions(long? taskId)
+ return Ok("Решение успешно добавлено в очередь на проверку!");
+ }
+
+ [HttpPost("rateEmptySolution/{taskId}")]
+ [Authorize(Roles = Roles.LecturerOrExpertRole)]
+ public async Task PostEmptySolutionWithRate(long taskId, SolutionViewModel solution)
+ {
+ var course = await _coursesServiceClient.GetCourseByTask(taskId);
+ if (course == null || !course.MentorIds.Contains(UserId)) return Forbid();
+ if (course.CourseMates.All(t => t.StudentId != solution.StudentId))
+ return BadRequest($"Студент с id {solution.StudentId} не записан на курс");
+
+ solution.Comment = "[Решение было сдано вне сервиса]";
+ await _solutionsClient.PostEmptySolutionWithRate(taskId, solution);
+ return Ok();
+ }
+
+ [HttpPost("giveUp/{taskId}")]
+ [Authorize(Roles = Roles.StudentRole)]
+ public async Task GiveUp(long taskId)
+ {
+ var course = await _coursesServiceClient.GetCourseByTask(taskId);
+ if (course == null) return NotFound();
+ if (course.CourseMates.All(t => t.StudentId != UserId))
+ return BadRequest($"Студент с id {UserId} не записан на курс");
+
+ await _solutionsClient.PostEmptySolutionWithRate(taskId, new SolutionViewModel
{
- var mentorCourses = await _coursesServiceClient.GetAllUserCourses();
- var tasks = FilterTasks(mentorCourses, taskId).ToDictionary(t => t.taskId, t => t.data);
+ StudentId = UserId,
+ Comment = "[Студент отказался от выполнения задачи]",
+ Rating = 0
+ });
+ return Ok();
+ }
- var studentsAndTasks = new Dictionary studentIds, List taskIds)>();
- foreach (var value in tasks.Values)
- {
- studentsAndTasks.TryAdd(
- value.course.Id, (
- value.course.AcceptedStudents.Select(ast => ast.StudentId).ToList(),
- new List()));
- studentsAndTasks[value.course.Id].taskIds.Add(value.task.Id);
- }
+ [HttpPost("rateSolution/{solutionId}")]
+ [Authorize(Roles = Roles.LecturerOrExpertRole)]
+ public async Task RateSolution(long solutionId,
+ RateSolutionModel rateSolutionModel)
+ {
+ await _solutionsClient.RateSolution(solutionId, rateSolutionModel);
+ return Ok();
+ }
- var solutions = await GetAllUnratedSolutionsForTasks(studentsAndTasks);
+ [HttpGet("actuality/{solutionId}")]
+ [ProducesResponseType(typeof(SolutionActualityDto), (int)HttpStatusCode.OK)]
+ public async Task GetSolutionActuality(long solutionId)
+ {
+ var result = await _solutionsClient.GetSolutionActuality(solutionId);
+ return Ok(result);
+ }
- var studentIds = solutions.Select(t => t.StudentId).Distinct().ToArray();
- var accountsData = await AuthServiceClient.GetAccountsData(studentIds);
+ [HttpPost("markSolutionFinal/{solutionId}")]
+ [Authorize(Roles = Roles.LecturerRole)]
+ public async Task MarkSolution(long solutionId)
+ {
+ await _solutionsClient.MarkSolution(solutionId);
+ return Ok();
+ }
- var unratedSolutions = solutions
- .Join(accountsData, s => s.StudentId, s => s.UserId, (solution, account) =>
- {
- var (course, homeworkTitle, task) = tasks[solution.TaskId];
- return new SolutionPreviewView
- {
- SolutionId = solution.SolutionId,
- Student = account,
- CourseTitle = $"{course.Name} / {course.GroupName}",
- CourseId = course.Id,
- HomeworkTitle = homeworkTitle,
- TaskTitle = task.Title,
- TaskId = task.Id,
- PublicationDate = solution.PublicationDate,
- IsFirstTry = solution.IsFirstTry,
- GroupId = solution.GroupId,
- SentAfterDeadline = solution.IsFirstTry && task.DeadlineDate != null &&
- solution.PublicationDate > task.DeadlineDate,
- IsCourseCompleted = course.IsCompleted
- };
- })
- .ToArray();
-
- return new UnratedSolutionPreviews
- {
- UnratedSolutions = unratedSolutions,
- };
- }
+ [HttpDelete("delete/{solutionId}")]
+ [Authorize(Roles = Roles.LecturerRole)]
+ public async Task DeleteSolution(long solutionId)
+ {
+ await _solutionsClient.DeleteSolution(solutionId);
+ return Ok();
+ }
+
+ [HttpGet("unratedSolutions")]
+ [Authorize(Roles = Roles.LecturerOrExpertRole)]
+ public async Task GetUnratedSolutions(long? taskId)
+ {
+ var mentorCourses = await _coursesServiceClient.GetAllUserCourses();
+ var tasks = FilterTasks(mentorCourses, taskId).ToDictionary(t => t.taskId, t => t.data);
- [Authorize]
- [HttpGet("solutionAchievement")]
- [ProducesResponseType(typeof(int), (int)HttpStatusCode.OK)]
- public async Task GetSolutionAchievement(long taskId, long solutionId)
+ var studentsAndTasks = new Dictionary studentIds, List taskIds)>();
+ foreach (var value in tasks.Values)
{
- var course = await _coursesServiceClient.GetCourseByTask(taskId);
- if (course is null) return BadRequest();
-
- var isMentor = course.MentorIds.Contains(UserId);
- if (!isMentor &&
- course.AcceptedStudents.FirstOrDefault(t => t.StudentId == UserId) == null)
- return BadRequest($"Студента или преподавателя с id {UserId} не существует");
-
- var solutions = await _solutionsClient.GetTaskSolutionStatistics(course.Id, taskId);
- var lastRatedSolutions = solutions
- .Select(t => t.Solutions.LastOrDefault(x => x.State != SolutionState.Posted))
- .Where(t => t != null)
- .ToArray();
-
- var solution = lastRatedSolutions.FirstOrDefault(t => t!.Id == solutionId &&
- (isMentor || t.StudentId == UserId));
- if (solution == null) return NotFound();
-
- if (lastRatedSolutions.Any(x => x!.GroupId != null))
- lastRatedSolutions = lastRatedSolutions.DistinctBy(t => t!.Id).ToArray();
-
- var betterThanCount = lastRatedSolutions.Count(t => solution.Rating > t!.Rating);
- if (betterThanCount == 0) return Ok(lastRatedSolutions.Length == 1 ? 100 : 0);
- return Ok(betterThanCount * 100 / (lastRatedSolutions.Length - 1));
+ studentsAndTasks.TryAdd(
+ value.course.Id, (
+ value.course.AcceptedStudents.Select(ast => ast.StudentId).ToList(),
+ new List()));
+ studentsAndTasks[value.course.Id].taskIds.Add(value.task.Id);
}
- private async Task GetAllUnratedSolutionsForTasks(
- Dictionary studentIds, List taskIds)> tasksAndStudents)
- {
- var solutions = new List>();
+ var solutions = await GetAllUnratedSolutionsForTasks(studentsAndTasks);
- foreach (var value in tasksAndStudents.Values)
+ var studentIds = solutions.Select(t => t.StudentId).Distinct().ToArray();
+ var accountsData = await AuthServiceClient.GetAccountsData(studentIds);
+
+ var unratedSolutions = solutions
+ .Join(accountsData, s => s.StudentId, s => s.UserId, (solution, account) =>
{
- solutions.Add(
- _solutionsClient.GetAllUnratedSolutionsForTasks(
- new GetTasksSolutionsModel
- {
- TaskIds = value.taskIds.ToArray(),
- StudentIds = value.studentIds.ToArray()
- }
- )
- );
- }
+ var (course, homeworkTitle, task) = tasks[solution.TaskId];
+ return new SolutionPreviewView
+ {
+ SolutionId = solution.SolutionId,
+ Student = account,
+ CourseTitle = $"{course.Name} / {course.GroupName}",
+ CourseId = course.Id,
+ HomeworkTitle = homeworkTitle,
+ TaskTitle = task.Title,
+ TaskId = task.Id,
+ PublicationDate = solution.PublicationDate,
+ IsFirstTry = solution.IsFirstTry,
+ GroupId = solution.GroupId,
+ SentAfterDeadline = solution.IsFirstTry && task.DeadlineDate != null &&
+ solution.PublicationDate > task.DeadlineDate,
+ IsCourseCompleted = course.IsCompleted,
+ IsTest = task.Tags.Contains(HomeworkTags.Test)
+ };
+ })
+ .ToArray();
- var allSolutions = await Task.WhenAll(solutions);
- return allSolutions.SelectMany(s => s).OrderBy(s => s.PublicationDate).ToArray();
- }
+ return new UnratedSolutionPreviews
+ {
+ UnratedSolutions = unratedSolutions
+ };
+ }
- private static IEnumerable<(long taskId,
- (CourseDTO course, string homeworkTitle, HomeworkTaskViewModel task) data)>
- FilterTasks(CourseDTO[] courses, long? taskId)
+ [Authorize]
+ [HttpGet("solutionAchievement")]
+ [ProducesResponseType(typeof(int), (int)HttpStatusCode.OK)]
+ public async Task GetSolutionAchievement(long taskId, long solutionId)
+ {
+ var course = await _coursesServiceClient.GetCourseByTask(taskId);
+ if (course is null) return BadRequest();
+
+ var isMentor = course.MentorIds.Contains(UserId);
+ if (!isMentor &&
+ course.AcceptedStudents.FirstOrDefault(t => t.StudentId == UserId) == null)
+ return BadRequest($"Студента или преподавателя с id {UserId} не существует");
+
+ var solutions = await _solutionsClient.GetTaskSolutionStatistics(course.Id, taskId);
+ var lastRatedSolutions = solutions
+ .Select(t => t.Solutions.LastOrDefault(x => x.State != SolutionState.Posted))
+ .Where(t => t != null)
+ .ToArray();
+
+ var solution = lastRatedSolutions.FirstOrDefault(t => t!.Id == solutionId &&
+ (isMentor || t.StudentId == UserId));
+ if (solution == null) return NotFound();
+
+ if (lastRatedSolutions.Any(x => x!.GroupId != null))
+ lastRatedSolutions = lastRatedSolutions.DistinctBy(t => t!.Id).ToArray();
+
+ var betterThanCount = lastRatedSolutions.Count(t => solution.Rating > t!.Rating);
+ if (betterThanCount == 0) return Ok(lastRatedSolutions.Length == 1 ? 100 : 0);
+ return Ok(betterThanCount * 100 / (lastRatedSolutions.Length - 1));
+ }
+
+ private async Task GetAllUnratedSolutionsForTasks(
+ Dictionary studentIds, List taskIds)> tasksAndStudents)
+ {
+ var solutions = new List>();
+
+ foreach (var value in tasksAndStudents.Values)
+ solutions.Add(
+ _solutionsClient.GetAllUnratedSolutionsForTasks(
+ new GetTasksSolutionsModel
+ {
+ TaskIds = value.taskIds.ToArray(),
+ StudentIds = value.studentIds.ToArray()
+ }
+ )
+ );
+
+ var allSolutions = await Task.WhenAll(solutions);
+ return allSolutions.SelectMany(s => s).OrderBy(s => s.PublicationDate).ToArray();
+ }
+
+ private static IEnumerable<(long taskId,
+ (CourseDTO course, string homeworkTitle, HomeworkTaskViewModel task) data)>
+ FilterTasks(CourseDTO[] courses, long? taskId)
+ {
+ foreach (var course in courses)
+ foreach (var homework in course.Homeworks)
+ foreach (var task in homework.Tasks)
{
- foreach (var course in courses)
- foreach (var homework in course.Homeworks)
- foreach (var task in homework.Tasks)
+ if (taskId is { } id && task.Id == id)
{
- if (taskId is { } id && task.Id == id)
- {
- yield return (task.Id, (course, homework.Title, task));
- yield break;
- }
-
- if (!taskId.HasValue)
- yield return (task.Id, (course, homework.Title, task));
+ yield return (task.Id, (course, homework.Title, task));
+ yield break;
}
- }
- private static (string id, string tasks) GetGroupingKey(HomeworkViewModel homework)
- {
- var isTest = homework.Tags.Contains(HomeworkTags.Test);
- var groupingTag = homework.Tags.Except(HomeworkTags.DefaultTags).FirstOrDefault();
- return isTest && groupingTag != null
- ? (groupingTag, string.Join(";", homework.Tasks.Select(t => t.MaxRating)))
- : (homework.Id.ToString(), "");
+ if (!taskId.HasValue)
+ yield return (task.Id, (course, homework.Title, task));
}
}
+
+ private static (string id, string tasks) GetGroupingKey(HomeworkViewModel homework)
+ {
+ var isTest = homework.Tags.Contains(HomeworkTags.Test);
+ var groupingTag = homework.Tags.Except(HomeworkTags.DefaultTags).FirstOrDefault();
+ return isTest && groupingTag != null
+ ? (groupingTag, string.Join(";", homework.Tasks.Select(t => t.MaxRating)))
+ : (homework.Id.ToString(), "");
+ }
}
diff --git a/HwProj.APIGateway/HwProj.APIGateway.API/Controllers/StatisticsController.cs b/HwProj.APIGateway/HwProj.APIGateway.API/Controllers/StatisticsController.cs
index 0f0617a02..dfd6c567f 100644
--- a/HwProj.APIGateway/HwProj.APIGateway.API/Controllers/StatisticsController.cs
+++ b/HwProj.APIGateway/HwProj.APIGateway.API/Controllers/StatisticsController.cs
@@ -14,143 +14,142 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
-namespace HwProj.APIGateway.API.Controllers
+namespace HwProj.APIGateway.API.Controllers;
+
+[Route("api/[controller]")]
+[ApiController]
+public class StatisticsController : AggregationController
{
- [Route("api/[controller]")]
- [ApiController]
- public class StatisticsController : AggregationController
+ private readonly ICoursesServiceClient _coursesClient;
+ private readonly ISolutionsServiceClient _solutionClient;
+
+ public StatisticsController(ISolutionsServiceClient solutionClient, IAuthServiceClient authServiceClient,
+ ICoursesServiceClient coursesServiceClient) :
+ base(authServiceClient)
{
- private readonly ISolutionsServiceClient _solutionClient;
- private readonly ICoursesServiceClient _coursesClient;
+ _solutionClient = solutionClient;
+ _coursesClient = coursesServiceClient;
+ }
- public StatisticsController(ISolutionsServiceClient solutionClient, IAuthServiceClient authServiceClient,
- ICoursesServiceClient coursesServiceClient) :
- base(authServiceClient)
- {
- _solutionClient = solutionClient;
- _coursesClient = coursesServiceClient;
- }
-
- [HttpGet("{courseId}/lecturers")]
- [Authorize(Roles = Roles.LecturerRole)]
- [ProducesResponseType(typeof(StatisticsLecturersModel[]), (int)HttpStatusCode.OK)]
- public async Task GetLecturersStatistics(long courseId)
+ [HttpGet("{courseId}/lecturers")]
+ [Authorize(Roles = Roles.LecturerRole)]
+ [ProducesResponseType(typeof(StatisticsLecturersModel[]), (int)HttpStatusCode.OK)]
+ public async Task GetLecturersStatistics(long courseId)
+ {
+ var statistics = await _solutionClient.GetLecturersStatistics(courseId);
+ if (statistics == null)
+ return NotFound();
+
+ var lecturers = await AuthServiceClient.GetAccountsData(statistics.Select(s => s.LecturerId).ToArray());
+
+ var result = statistics.Zip(lecturers, (stat, lecturer) => new StatisticsLecturersModel
{
- var statistics = await _solutionClient.GetLecturersStatistics(courseId);
- if (statistics == null)
- return NotFound();
+ Lecturer = lecturer,
+ NumberOfCheckedSolutions = stat.NumberOfCheckedSolutions,
+ NumberOfCheckedUniqueSolutions = stat.NumberOfCheckedUniqueSolutions
+ }).ToArray();
- var lecturers = await AuthServiceClient.GetAccountsData(statistics.Select(s => s.LecturerId).ToArray());
+ return Ok(result);
+ }
- var result = statistics.Zip(lecturers, (stat, lecturer) => new StatisticsLecturersModel
- {
- Lecturer = lecturer,
- NumberOfCheckedSolutions = stat.NumberOfCheckedSolutions,
- NumberOfCheckedUniqueSolutions = stat.NumberOfCheckedUniqueSolutions
- }).ToArray();
+ [HttpGet("{courseId}")]
+ [ProducesResponseType(typeof(StatisticsCourseMatesModel[]), (int)HttpStatusCode.OK)]
+ public async Task GetCourseStatistics(long courseId)
+ {
+ var statistics = await _solutionClient.GetCourseStatistics(courseId, UserId);
+ if (statistics == null) return Forbid();
- return Ok(result);
- }
+ var studentIds = statistics.Select(t => t.StudentId).ToArray();
+ var getStudentsTask = AuthServiceClient.GetAccountsData(studentIds);
- [HttpGet("{courseId}")]
- [ProducesResponseType(typeof(StatisticsCourseMatesModel[]), (int)HttpStatusCode.OK)]
- public async Task GetCourseStatistics(long courseId)
- {
- var statistics = await _solutionClient.GetCourseStatistics(courseId, UserId);
- if (statistics == null) return Forbid();
-
- var studentIds = statistics.Select(t => t.StudentId).ToArray();
- var getStudentsTask = AuthServiceClient.GetAccountsData(studentIds);
-
- // Получаем пары <студент, закрепленные преподаватели (те, которые его явно в фильтре выбрали)>
- var mentorsToStudents = await _coursesClient.GetMentorsToAssignedStudents(courseId);
- var studentsToMentors = await GetStudentsToMentorsDictionary(mentorsToStudents);
-
- var result = statistics.Zip(
- await getStudentsTask,
- (stats, student) =>
+ // Получаем пары <студент, закрепленные преподаватели (те, которые его явно в фильтре выбрали)>
+ var mentorsToStudents = await _coursesClient.GetMentorsToAssignedStudents(courseId);
+ var studentsToMentors = await GetStudentsToMentorsDictionary(mentorsToStudents);
+
+ var result = statistics.Zip(
+ await getStudentsTask,
+ (stats, student) =>
+ {
+ studentsToMentors.TryGetValue(student.UserId, out var reviewers);
+ return new StatisticsCourseMatesModel
{
- studentsToMentors.TryGetValue(student.UserId, out var reviewers);
- return new StatisticsCourseMatesModel()
- {
- Id = student.UserId,
- Name = student.Name,
- Surname = student.Surname,
- Reviewers = reviewers ?? Array.Empty(),
- Homeworks = stats.Homeworks
- };
- }).OrderBy(t => t.Surname).ThenBy(t => t.Name);
-
- return Ok(result);
- }
-
- [HttpGet("{courseId}/charts")]
- [ProducesResponseType(typeof(AdvancedCourseStatisticsViewModel), (int)HttpStatusCode.OK)]
- public async Task GetChartStatistics(long courseId)
- {
- var course = await _coursesClient.GetCourseById(courseId);
- if (course == null)
- return Forbid();
+ Id = student.UserId,
+ Name = student.Name,
+ Surname = student.Surname,
+ Reviewers = reviewers ?? Array.Empty(),
+ Homeworks = stats.Homeworks
+ };
+ }).OrderBy(t => t.Surname).ThenBy(t => t.Name);
- var statistics = await _solutionClient.GetCourseStatistics(courseId, UserId);
- var studentIds = statistics.Select(t => t.StudentId).ToArray();
- var studentsData = await AuthServiceClient.GetAccountsData(studentIds);
+ return Ok(result);
+ }
- var students = statistics.Zip(studentsData,
- (stats, student) => new StatisticsCourseMatesModel
+ [HttpGet("{courseId}/charts")]
+ [ProducesResponseType(typeof(AdvancedCourseStatisticsViewModel), (int)HttpStatusCode.OK)]
+ public async Task GetChartStatistics(long courseId)
+ {
+ var course = await _coursesClient.GetCourseById(courseId);
+ if (course == null)
+ return Forbid();
+
+ var statistics = await _solutionClient.GetCourseStatistics(courseId, UserId);
+ var studentIds = statistics.Select(t => t.StudentId).ToArray();
+ var studentsData = await AuthServiceClient.GetAccountsData(studentIds);
+
+ var students = statistics.Zip(studentsData,
+ (stats, student) => new StatisticsCourseMatesModel
{
Id = student.UserId,
Name = student.Name,
Surname = student.Surname,
Homeworks = stats.Homeworks
}).OrderBy(t => t.Surname).ThenBy(t => t.Name);
-
- var statisticsMeasure = await _solutionClient.GetBenchmarkStatistics(courseId);
- var result = new AdvancedCourseStatisticsViewModel
- {
- Course = new CoursePreview
- {
- Id = course.Id,
- Name = course.Name,
- GroupName = course.GroupName,
- },
- StudentStatistics = students.ToArray(),
- Homeworks = course.Homeworks,
- AverageStudentSolutions = statisticsMeasure.AverageStudentSolutions,
- BestStudentSolutions = statisticsMeasure.BestStudentSolutions
- };
-
- return Ok(result);
- }
-
- private async Task> GetStudentsToMentorsDictionary(
- MentorToAssignedStudentsDTO[] mentorsToStudents)
+ var statisticsMeasure = await _solutionClient.GetBenchmarkStatistics(courseId);
+
+ var result = new AdvancedCourseStatisticsViewModel
{
- var mentorsIds = mentorsToStudents.Select(mts => mts.MentorId).ToArray();
- var mentorsAccountData = await AuthServiceClient.GetAccountsData(mentorsIds);
- var mentorIdToAccountData = mentorsAccountData
- .ToDictionary(
- accountData => accountData.UserId,
- accountData => accountData
- );
-
- return mentorsToStudents
- .SelectMany(m =>
- m.SelectedStudentsIds.Select(studentId =>
- new
- {
- StudentId = studentId,
- Reviewer = mentorIdToAccountData[m.MentorId]
- })
- )
- .GroupBy(sr => sr.StudentId)
- .ToDictionary(
- groups => groups.Key,
- groups => groups.Select(sr => sr.Reviewer)
- .Distinct()
- .ToArray()
- );
- }
+ Course = new CoursePreview
+ {
+ Id = course.Id,
+ Name = course.Name,
+ GroupName = course.GroupName
+ },
+ StudentStatistics = students.ToArray(),
+ Homeworks = course.Homeworks,
+ AverageStudentSolutions = statisticsMeasure.AverageStudentSolutions,
+ BestStudentSolutions = statisticsMeasure.BestStudentSolutions
+ };
+
+ return Ok(result);
+ }
+
+ private async Task> GetStudentsToMentorsDictionary(
+ MentorToAssignedStudentsDTO[] mentorsToStudents)
+ {
+ var mentorsIds = mentorsToStudents.Select(mts => mts.MentorId).ToArray();
+ var mentorsAccountData = await AuthServiceClient.GetAccountsData(mentorsIds);
+ var mentorIdToAccountData = mentorsAccountData
+ .ToDictionary(
+ accountData => accountData.UserId,
+ accountData => accountData
+ );
+
+ return mentorsToStudents
+ .SelectMany(m =>
+ m.SelectedStudentsIds.Select(studentId =>
+ new
+ {
+ StudentId = studentId,
+ Reviewer = mentorIdToAccountData[m.MentorId]
+ })
+ )
+ .GroupBy(sr => sr.StudentId)
+ .ToDictionary(
+ groups => groups.Key,
+ groups => groups.Select(sr => sr.Reviewer)
+ .Distinct()
+ .ToArray()
+ );
}
-}
\ No newline at end of file
+}
diff --git a/HwProj.APIGateway/HwProj.APIGateway.API/Controllers/SystemController.cs b/HwProj.APIGateway/HwProj.APIGateway.API/Controllers/SystemController.cs
index 61fb17326..233bd6ed4 100644
--- a/HwProj.APIGateway/HwProj.APIGateway.API/Controllers/SystemController.cs
+++ b/HwProj.APIGateway/HwProj.APIGateway.API/Controllers/SystemController.cs
@@ -1,65 +1,74 @@
using System.Threading.Tasks;
using HwProj.APIGateway.API.Models;
using HwProj.AuthService.Client;
+using HwProj.ContentService.Client;
using HwProj.CoursesService.Client;
using HwProj.NotificationsService.Client;
using HwProj.SolutionsService.Client;
using Microsoft.AspNetCore.Mvc;
-namespace HwProj.APIGateway.API.Controllers
+namespace HwProj.APIGateway.API.Controllers;
+
+[Route("api/[controller]")]
+[ApiController]
+public class SystemController : AggregationController
{
- [Route("api/[controller]")]
- [ApiController]
- public class SystemController : AggregationController
- {
- private readonly ICoursesServiceClient _coursesServiceClient;
- private readonly INotificationsServiceClient _notificationsServiceClient;
- private readonly ISolutionsServiceClient _solutionsServiceClient;
+ private readonly IContentServiceClient _contentServiceClient;
+ private readonly ICoursesServiceClient _coursesServiceClient;
+ private readonly INotificationsServiceClient _notificationsServiceClient;
+ private readonly ISolutionsServiceClient _solutionsServiceClient;
- public SystemController(
- IAuthServiceClient authServiceClient,
- ICoursesServiceClient coursesServiceClient,
- INotificationsServiceClient notificationsServiceClient,
- ISolutionsServiceClient solutionsServiceClient) : base(authServiceClient)
- {
- _coursesServiceClient = coursesServiceClient;
- _notificationsServiceClient = notificationsServiceClient;
- _solutionsServiceClient = solutionsServiceClient;
- }
+ public SystemController(
+ IAuthServiceClient authServiceClient,
+ ICoursesServiceClient coursesServiceClient,
+ INotificationsServiceClient notificationsServiceClient,
+ ISolutionsServiceClient solutionsServiceClient,
+ IContentServiceClient contentServiceClient) : base(authServiceClient)
+ {
+ _coursesServiceClient = coursesServiceClient;
+ _notificationsServiceClient = notificationsServiceClient;
+ _solutionsServiceClient = solutionsServiceClient;
+ _contentServiceClient = contentServiceClient;
+ }
- [HttpGet("status")]
- public async Task Status()
- {
- var authPing = AuthServiceClient.Ping();
- var coursesPing = _coursesServiceClient.Ping();
- var notificationsPing = _notificationsServiceClient.Ping();
- var solutionsPing = _solutionsServiceClient.Ping();
+ [HttpGet("status")]
+ public async Task Status()
+ {
+ var authPing = AuthServiceClient.Ping();
+ var coursesPing = _coursesServiceClient.Ping();
+ var notificationsPing = _notificationsServiceClient.Ping();
+ var solutionsPing = _solutionsServiceClient.Ping();
+ var filesPing = _contentServiceClient.Ping();
- await Task.WhenAll(authPing, coursesPing, notificationsPing, solutionsPing);
+ await Task.WhenAll(authPing, coursesPing, notificationsPing, solutionsPing);
- return new[]
+ return new[]
+ {
+ new SystemInfo
+ {
+ Service = "Auth Service",
+ IsAvailable = authPing.Result
+ },
+ new SystemInfo
+ {
+ Service = "Courses Service",
+ IsAvailable = coursesPing.Result
+ },
+ new SystemInfo
+ {
+ Service = "Notifications Service",
+ IsAvailable = notificationsPing.Result
+ },
+ new SystemInfo
+ {
+ Service = "Solutions Service",
+ IsAvailable = solutionsPing.Result
+ },
+ new SystemInfo
{
- new SystemInfo
- {
- Service = "Auth Service",
- IsAvailable = authPing.Result
- },
- new SystemInfo
- {
- Service = "Courses Service",
- IsAvailable = coursesPing.Result
- },
- new SystemInfo
- {
- Service = "Notifications Service",
- IsAvailable = notificationsPing.Result
- },
- new SystemInfo
- {
- Service = "Solutions Service",
- IsAvailable = solutionsPing.Result
- },
- };
- }
+ Service = "Content Service",
+ IsAvailable = filesPing.Result
+ }
+ };
}
}
diff --git a/HwProj.APIGateway/HwProj.APIGateway.API/Controllers/TasksController.cs b/HwProj.APIGateway/HwProj.APIGateway.API/Controllers/TasksController.cs
index 938b86d4b..deabf55cd 100644
--- a/HwProj.APIGateway/HwProj.APIGateway.API/Controllers/TasksController.cs
+++ b/HwProj.APIGateway/HwProj.APIGateway.API/Controllers/TasksController.cs
@@ -4,95 +4,95 @@
using HwProj.Models.CoursesService.ViewModels;
using HwProj.Models.Result;
using HwProj.Models.Roles;
-using HwProj.Utils.Authorization;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
-namespace HwProj.APIGateway.API.Controllers
+namespace HwProj.APIGateway.API.Controllers;
+
+[Route("api/[controller]")]
+[ApiController]
+public class TasksController(ICoursesServiceClient coursesClient) : ControllerBase
{
- [Route("api/[controller]")]
- [ApiController]
- public class TasksController : ControllerBase
+ [HttpGet("get/{taskId}")]
+ [Authorize]
+ [ProducesResponseType(typeof(HomeworkTaskViewModel), (int)HttpStatusCode.OK)]
+ public async Task GetTask(long taskId, [FromQuery] bool? withCriteria)
{
- private readonly ICoursesServiceClient _coursesClient;
-
- public TasksController(ICoursesServiceClient coursesClient)
- {
- _coursesClient = coursesClient;
- }
+ var result = await coursesClient.GetTask(taskId, withCriteria ?? false);
+ return result == null
+ ? NotFound()
+ : Ok(result);
+ }
- [HttpGet("get/{taskId}")]
- [Authorize]
- [ProducesResponseType(typeof(HomeworkTaskViewModel), (int)HttpStatusCode.OK)]
- public async Task GetTask(long taskId)
- {
- var result = await _coursesClient.GetTask(taskId);
- return result == null
- ? NotFound() as IActionResult
- : Ok(result);
- }
+ [HttpGet("getForEditing/{taskId}")]
+ [Authorize(Roles = Roles.LecturerRole)]
+ [ProducesResponseType(typeof(HomeworkTaskForEditingViewModel), (int)HttpStatusCode.OK)]
+ public async Task GetForEditingTask(long taskId)
+ {
+ var result = await coursesClient.GetForEditingTask(taskId);
+ return result == null
+ ? NotFound()
+ : Ok(result);
+ }
- [HttpGet("getForEditing/{taskId}")]
- [Authorize(Roles = Roles.LecturerRole)]
- [ProducesResponseType(typeof(HomeworkTaskForEditingViewModel), (int)HttpStatusCode.OK)]
- public async Task GetForEditingTask(long taskId)
- {
- var result = await _coursesClient.GetForEditingTask(taskId);
- return result == null
- ? NotFound() as IActionResult
- : Ok(result);
- }
+ [HttpPost("add/{homeworkId}")]
+ [Authorize(Roles = Roles.LecturerRole)]
+ [ProducesResponseType(typeof(Result), (int)HttpStatusCode.OK)]
+ public async Task AddTask(long homeworkId, PostTaskViewModel taskViewModel)
+ {
+ var result = await coursesClient.AddTask(homeworkId, taskViewModel);
+ return result.Succeeded
+ ? Ok(result)
+ : BadRequest(result);
+ }
- [HttpPost("add/{homeworkId}")]
- [Authorize(Roles = Roles.LecturerRole)]
- [ProducesResponseType(typeof(long), (int)HttpStatusCode.OK)]
- public async Task AddTask(long homeworkId, CreateTaskViewModel taskViewModel)
- {
- var result = await _coursesClient.AddTask(homeworkId, taskViewModel);
- return result.Succeeded
- ? Ok(result.Value) as IActionResult
- : BadRequest(result.Errors);
- }
+ [HttpDelete("delete/{taskId}")]
+ [Authorize(Roles = Roles.LecturerRole)]
+ public async Task DeleteTask(long taskId)
+ {
+ await coursesClient.DeleteTask(taskId);
+ return Ok();
+ }
- [HttpDelete("delete/{taskId}")]
- [Authorize(Roles = Roles.LecturerRole)]
- public async Task DeleteTask(long taskId)
- {
- await _coursesClient.DeleteTask(taskId);
- return Ok();
- }
+ [HttpPut("update/{taskId}")]
+ [Authorize(Roles = Roles.LecturerRole)]
+ [ProducesResponseType(typeof(Result), (int)HttpStatusCode.OK)]
+ public async Task UpdateTask(long taskId, PostTaskViewModel taskViewModel)
+ {
+ var result = await coursesClient.UpdateTask(taskId, taskViewModel);
+ return Ok(result);
+ }
- [HttpPut("update/{taskId}")]
- [Authorize(Roles = Roles.LecturerRole)]
- [ProducesResponseType(typeof(Result), (int)HttpStatusCode.OK)]
- public async Task UpdateTask(long taskId, CreateTaskViewModel taskViewModel)
- {
- var result = await _coursesClient.UpdateTask(taskId, taskViewModel);
- return Ok(result);
- }
+ [HttpPost("addQuestion")]
+ [Authorize(Roles = Roles.StudentRole)]
+ public async Task AddQuestionForTask(AddTaskQuestionDto question)
+ {
+ await coursesClient.AddQuestionForTask(question);
+ return Ok();
+ }
- [HttpPost("addQuestion")]
- [Authorize(Roles = Roles.StudentRole)]
- public async Task AddQuestionForTask(AddTaskQuestionDto question)
- {
- await _coursesClient.AddQuestionForTask(question);
- return Ok();
- }
+ [HttpGet("questions/{taskId}")]
+ [ProducesResponseType(typeof(GetTaskQuestionDto[]), (int)HttpStatusCode.OK)]
+ public async Task GetQuestionsForTask(long taskId)
+ {
+ var result = await coursesClient.GetQuestionsForTask(taskId);
+ return Ok(result);
+ }
- [HttpGet("questions/{taskId}")]
- [ProducesResponseType(typeof(GetTaskQuestionDto[]), (int)HttpStatusCode.OK)]
- public async Task GetQuestionsForTask(long taskId)
- {
- var result = await _coursesClient.GetQuestionsForTask(taskId);
- return Ok(result);
- }
+ [HttpPost("addAnswer")]
+ [Authorize(Roles = Roles.LecturerOrExpertRole)]
+ public async Task AddAnswerForQuestion(AddAnswerForQuestionDto answer)
+ {
+ await coursesClient.AddAnswerForQuestion(answer);
+ return Ok();
+ }
- [HttpPost("addAnswer")]
- [Authorize(Roles = Roles.LecturerOrExpertRole)]
- public async Task AddAnswerForQuestion(AddAnswerForQuestionDto answer)
- {
- await _coursesClient.AddAnswerForQuestion(answer);
- return Ok();
- }
+ [HttpGet("openQuestions")]
+ [Authorize(Roles = Roles.LecturerOrExpertRole)]
+ [ProducesResponseType(typeof(QuestionsSummary[]), (int)HttpStatusCode.OK)]
+ public async Task GetOpenQuestions()
+ {
+ var result = await coursesClient.GetOpenQuestions();
+ return Ok(result);
}
}
diff --git a/HwProj.APIGateway/HwProj.APIGateway.API/Dockerfile b/HwProj.APIGateway/HwProj.APIGateway.API/Dockerfile
index be8d9281e..dc0142c63 100644
--- a/HwProj.APIGateway/HwProj.APIGateway.API/Dockerfile
+++ b/HwProj.APIGateway/HwProj.APIGateway.API/Dockerfile
@@ -1,4 +1,4 @@
-FROM mcr.microsoft.com/dotnet/core/sdk:2.2-stretch AS build
+FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /src
COPY ["Directory.Build.props", "Directory.Build.props"]
@@ -16,13 +16,12 @@ COPY ["HwProj.SolutionsService/HwProj.SolutionsService.Client/", "HwProj.Solutio
COPY ["HwProj.Common/HwProj.Exceptions/", "HwProj.Common/HwProj.Exceptions/"]
COPY ["HwProj.ContentService/HwProj.ContentService.Client/", "HwProj.ContentService/HwProj.ContentService.Client/"]
COPY ["HwProj.StudentInfo/IStudentsInfo/", "HwProj.StudentInfo/IStudentsInfo/"]
-COPY ["HwProj.StudentInfo/StudentsInfo.Tests/", "HwProj.StudentInfo/StudentsInfo.Tests/"]
COPY ["HwProj.StudentInfo/StudentsInfo/", "HwProj.StudentInfo/StudentsInfo/"]
WORKDIR "/src/HwProj.APIGateway/HwProj.APIGateway.API"
RUN dotnet publish "HwProj.APIGateway.API.csproj" -c Release -o /app/publish
-FROM mcr.microsoft.com/dotnet/core/aspnet:2.2-stretch-slim AS final
+FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS final
WORKDIR /app
COPY --from=build /app/publish .
diff --git a/HwProj.APIGateway/HwProj.APIGateway.API/ExceptionFilters/ForbiddenExceptionFilter.cs b/HwProj.APIGateway/HwProj.APIGateway.API/ExceptionFilters/ForbiddenExceptionFilter.cs
index 677cbaa64..a6947df45 100644
--- a/HwProj.APIGateway/HwProj.APIGateway.API/ExceptionFilters/ForbiddenExceptionFilter.cs
+++ b/HwProj.APIGateway/HwProj.APIGateway.API/ExceptionFilters/ForbiddenExceptionFilter.cs
@@ -1,19 +1,17 @@
-using Microsoft.AspNetCore.Mvc.Filters;
using System;
using HwProj.Exceptions;
+using Microsoft.AspNetCore.Mvc.Filters;
+namespace HwProj.APIGateway.API.ExceptionFilters;
-namespace HwProj.APIGateway.API.ExceptionFilters
+public class ForbiddenExceptionFilter : Attribute, IExceptionFilter
{
- public class ForbiddenExceptionFilter : Attribute, IExceptionFilter
+ public void OnException(ExceptionContext context)
{
- public void OnException(ExceptionContext context)
+ if (context.Exception is ForbiddenException)
{
- if (context.Exception is ForbiddenException)
- {
- context.ExceptionHandled = true;
- context.HttpContext.Response.StatusCode = 403;
- }
+ context.ExceptionHandled = true;
+ context.HttpContext.Response.StatusCode = 403;
}
}
-}
\ No newline at end of file
+}
diff --git a/HwProj.APIGateway/HwProj.APIGateway.API/Filters/CourseMentorOnlyAttribute.cs b/HwProj.APIGateway/HwProj.APIGateway.API/Filters/CourseMentorOnlyAttribute.cs
index 0b2871182..22eeb698d 100644
--- a/HwProj.APIGateway/HwProj.APIGateway.API/Filters/CourseMentorOnlyAttribute.cs
+++ b/HwProj.APIGateway/HwProj.APIGateway.API/Filters/CourseMentorOnlyAttribute.cs
@@ -1,67 +1,75 @@
using System.Linq;
using System.Threading.Tasks;
using HwProj.CoursesService.Client;
+using HwProj.Models.ContentService.DTO;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
-namespace HwProj.APIGateway.API.Filters
+namespace HwProj.APIGateway.API.Filters;
+
+public class CourseMentorOnlyAttribute : ActionFilterAttribute
{
- public class CourseMentorOnlyAttribute : ActionFilterAttribute
+ private readonly ICoursesServiceClient _coursesServiceClient;
+
+ public CourseMentorOnlyAttribute(ICoursesServiceClient coursesServiceClient)
{
- private readonly ICoursesServiceClient _coursesServiceClient;
+ _coursesServiceClient = coursesServiceClient;
+ }
- public CourseMentorOnlyAttribute(ICoursesServiceClient coursesServiceClient)
+ public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
+ {
+ var userId = context.HttpContext.User.Claims
+ .FirstOrDefault(claim => claim.Type.ToString() == "_id")?.Value;
+ if (userId == null)
{
- _coursesServiceClient = coursesServiceClient;
+ context.Result = new ContentResult
+ {
+ StatusCode = StatusCodes.Status403Forbidden,
+ Content = "В запросе не передан идентификатор пользователя",
+ ContentType = "application/json"
+ };
+ return;
}
- public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
- {
- var userId = context.HttpContext.User.Claims
- .FirstOrDefault(claim => claim.Type.ToString() == "_id")?.Value;
- if (userId == null)
- {
- context.Result = new ContentResult
- {
- StatusCode = StatusCodes.Status403Forbidden,
- Content = "В запросе не передан идентификатор пользователя",
- ContentType = "application/json"
- };
- return;
- }
-
- string[]? mentorIds = null;
-
- var courseId = GetValueFromRequest(context.HttpContext.Request, "courseId");
- if (courseId != null && long.TryParse(courseId, out var id))
- {
- mentorIds = await _coursesServiceClient.GetCourseLecturersIds(id);
- }
+ string[]? mentorIds = null;
+ long courseId = -1;
- if (mentorIds == null || !mentorIds.Contains(userId))
- {
- context.Result = new ContentResult
- {
- StatusCode = StatusCodes.Status403Forbidden,
- Content = "Недостаточно прав для работы с файлами: Вы не являетесь ментором на курсе",
- ContentType = "application/json"
- };
- return;
- }
+ // Для метода Process (параметр: processFilesDto)
+ if (context.ActionArguments.TryGetValue("processFilesDto", out var processFilesDto) &&
+ processFilesDto is ProcessFilesDTO dto)
+ courseId = dto.FilesScope.CourseId;
- await next.Invoke();
- }
-
- private static string? GetValueFromRequest(HttpRequest request, string key)
+ // Для метода GetStatuses (параметр: filesScope)
+ else if (context.ActionArguments.TryGetValue("filesScope", out var filesScope) &&
+ filesScope is ScopeDTO scope)
+ courseId = scope.CourseId;
+
+ if (courseId != -1)
+ mentorIds = await _coursesServiceClient.GetCourseLecturersIds(courseId);
+
+ if (mentorIds == null || !mentorIds.Contains(userId))
{
- if (request.Query.TryGetValue(key, out var queryValue))
- return queryValue.ToString();
-
- if (request.HasFormContentType && request.Form.TryGetValue(key, out var formValue))
- return formValue.ToString();
-
- return null;
+ context.Result = new ContentResult
+ {
+ StatusCode = StatusCodes.Status403Forbidden,
+ Content = "Недостаточно прав для работы с файлами: Вы не являетесь ментором на курсе",
+ ContentType = "application/json"
+ };
+ return;
}
+
+ await next.Invoke();
+ }
+
+ private static string? GetValueFromRequest(HttpRequest request, string key)
+ {
+ if (request.Query.TryGetValue(key, out var queryValue))
+ return queryValue.ToString();
+
+ if (request.HasFormContentType && request.Form.TryGetValue(key, out var formValue))
+ return formValue.ToString();
+
+ return null;
}
-}
\ No newline at end of file
+}
diff --git a/HwProj.APIGateway/HwProj.APIGateway.API/Filters/FilesCountLimiter.cs b/HwProj.APIGateway/HwProj.APIGateway.API/Filters/FilesCountLimiter.cs
new file mode 100644
index 000000000..2f470677a
--- /dev/null
+++ b/HwProj.APIGateway/HwProj.APIGateway.API/Filters/FilesCountLimiter.cs
@@ -0,0 +1,27 @@
+using System.Linq;
+using System.Threading.Tasks;
+using HwProj.ContentService.Client;
+using HwProj.Models.ContentService.DTO;
+using HwProj.Models.CourseUnitType;
+
+namespace HwProj.APIGateway.API.Filters;
+
+public class FilesCountLimiter(IContentServiceClient contentServiceClient)
+{
+ public const long MaxSolutionFiles = 5;
+
+ public async Task CheckCountLimit(ProcessFilesDTO processFilesDto)
+ {
+ if (processFilesDto.FilesScope.CourseUnitType == CourseUnitType.Homework) return true;
+
+ var existingStatuses = await contentServiceClient.GetFilesStatuses(processFilesDto.FilesScope);
+ if (!existingStatuses.Succeeded) return false;
+
+ var existingIds = existingStatuses.Value.Select(f => f.Id).ToList();
+ if (processFilesDto.DeletingFileIds.Any(id => !existingIds.Contains(id)))
+ return false;
+
+ return existingIds.Count + processFilesDto.NewFiles.Count - processFilesDto.DeletingFileIds.Count <=
+ MaxSolutionFiles;
+ }
+}
diff --git a/HwProj.APIGateway/HwProj.APIGateway.API/Filters/FilesPrivacyFilter.cs b/HwProj.APIGateway/HwProj.APIGateway.API/Filters/FilesPrivacyFilter.cs
new file mode 100644
index 000000000..89f979b9b
--- /dev/null
+++ b/HwProj.APIGateway/HwProj.APIGateway.API/Filters/FilesPrivacyFilter.cs
@@ -0,0 +1,71 @@
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using HwProj.CoursesService.Client;
+using HwProj.Models.ContentService.DTO;
+using HwProj.Models.CourseUnitType;
+using HwProj.SolutionsService.Client;
+
+namespace HwProj.APIGateway.API.Filters;
+
+public class FilesPrivacyFilter(
+ ICoursesServiceClient coursesServiceClient,
+ ISolutionsServiceClient solutionsServiceClient)
+{
+ private async Task> GetSolutionStudentIds(long solutionId)
+ {
+ var studentIds = new HashSet();
+ var solution = await solutionsServiceClient.GetSolutionById(solutionId);
+ studentIds.Add(solution.StudentId);
+
+ if (solution.GroupId is { } groupId)
+ {
+ var groups = await coursesServiceClient.GetGroupsById(groupId);
+ if (groups is [var group]) studentIds.UnionWith(group.StudentsIds.ToHashSet());
+ }
+
+ return studentIds;
+ }
+
+ public async Task CheckDownloadRights(string? userId, ScopeDTO fileScope)
+ {
+ if (userId == null) return false;
+
+ switch (fileScope.CourseUnitType)
+ {
+ case CourseUnitType.Homework:
+ return true;
+ case CourseUnitType.Solution:
+ {
+ var studentIds = await GetSolutionStudentIds(fileScope.CourseUnitId);
+ if (studentIds.Contains(userId)) return true;
+
+ var mentorIds = await coursesServiceClient.GetCourseLecturersIds(fileScope.CourseId);
+ return mentorIds.Contains(userId);
+ }
+ default:
+ return false;
+ }
+ }
+
+ public async Task CheckUploadRights(string? userId, ScopeDTO fileScope)
+ {
+ if (userId == null) return false;
+
+ switch (fileScope.CourseUnitType)
+ {
+ case CourseUnitType.Homework:
+ {
+ var mentorIds = await coursesServiceClient.GetCourseLecturersIds(fileScope.CourseId);
+ return mentorIds.Contains(userId);
+ }
+ case CourseUnitType.Solution:
+ {
+ var studentIds = await GetSolutionStudentIds(fileScope.CourseUnitId);
+ return studentIds.Contains(userId);
+ }
+ default:
+ return false;
+ }
+ }
+}
diff --git a/HwProj.APIGateway/HwProj.APIGateway.API/HwProj.APIGateway.API.csproj b/HwProj.APIGateway/HwProj.APIGateway.API/HwProj.APIGateway.API.csproj
index 7238b2966..8f29323ea 100644
--- a/HwProj.APIGateway/HwProj.APIGateway.API/HwProj.APIGateway.API.csproj
+++ b/HwProj.APIGateway/HwProj.APIGateway.API/HwProj.APIGateway.API.csproj
@@ -1,30 +1,29 @@
- netcoreapp2.2
+ net8.0
..\..\docker-compose.dcproj
Linux
..\..
- $(CSharpLanguageVersion)
$(NullableReferenceTypes)
+ true
+ $(NoWarn);1591
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
diff --git a/HwProj.APIGateway/HwProj.APIGateway.API/Models/CoursePreviewView.cs b/HwProj.APIGateway/HwProj.APIGateway.API/Models/CoursePreviewView.cs
index 1f9494ae4..6aadbf82c 100644
--- a/HwProj.APIGateway/HwProj.APIGateway.API/Models/CoursePreviewView.cs
+++ b/HwProj.APIGateway/HwProj.APIGateway.API/Models/CoursePreviewView.cs
@@ -1,23 +1,22 @@
using HwProj.Models.AuthService.DTO;
-namespace HwProj.APIGateway.API.Models
+namespace HwProj.APIGateway.API.Models;
+
+public class CoursePreviewView
{
- public class CoursePreviewView
- {
- public long Id { get; set; }
- public string Name { get; set; }
- public string GroupName { get; set; }
- public bool IsCompleted { get; set; }
- public AccountDataDto[] Mentors { get; set; }
- public long? TaskId { get; set; }
- }
+ public long Id { get; set; }
+ public string Name { get; set; }
+ public string GroupName { get; set; }
+ public bool IsCompleted { get; set; }
+ public AccountDataDto[] Mentors { get; set; }
+ public long? TaskId { get; set; }
+}
- public class CourseEvents
- {
- public long Id { get; set; }
- public string Name { get; set; }
- public string GroupName { get; set; }
- public bool IsCompleted { get; set; }
- public int NewStudentsCount { get; set; }
- }
+public class CourseEvents
+{
+ public long Id { get; set; }
+ public string Name { get; set; }
+ public string GroupName { get; set; }
+ public bool IsCompleted { get; set; }
+ public int NewStudentsCount { get; set; }
}
diff --git a/HwProj.APIGateway/HwProj.APIGateway.API/Models/Solutions/PostSolutionModel.cs b/HwProj.APIGateway/HwProj.APIGateway.API/Models/Solutions/PostSolutionModel.cs
new file mode 100644
index 000000000..545b0e8bc
--- /dev/null
+++ b/HwProj.APIGateway/HwProj.APIGateway.API/Models/Solutions/PostSolutionModel.cs
@@ -0,0 +1,55 @@
+using System.Text.Json.Serialization;
+
+namespace HwProj.APIGateway.API.Models.Solutions;
+
+public class PostSolutionModel
+{
+ public string? GithubUrl { get; set; }
+ public string? Comment { get; set; }
+ public string[]? GroupMateIds { get; set; }
+}
+
+public class PostAutomatedSolutionModel
+{
+ /// Идентификатор задачи: название или id на HwProj, в зависимости от параметра TaskIdType
+ public required string TaskId { get; init; }
+
+ /// Тип идентификатора задачи (TaskId): Title или Id на HwProj
+ [JsonConverter(typeof(JsonStringEnumConverter))]
+ public TaskIdType TaskIdType { get; init; } = TaskIdType.Id;
+
+ /// Идентификатор студента: ФИО (в любом порядке), id на HwProj или привязанный GitHub-логин;
+ /// в зависимости от параметра StudentIdType
+ public required string StudentId { get; init; }
+
+ /// Тип идентификатора студента (StudentId): ФИО (в любом порядке), id на HwProj или привязанный GitHub-логин
+ [JsonConverter(typeof(JsonStringEnumConverter))]
+ public StudentIdType StudentIdType { get; init; } = StudentIdType.Id;
+
+ /// Ссылка на решение, будь то PR, репозиторий или другой источник
+ public string? GithubUrl { get; init; }
+
+ /// Комментарий к решению, здесь можно оставить полезную информацию, которая будет отображаться при проверке решения в сервисе
+ public string? Comment { get; init; }
+}
+
+public enum TaskIdType
+{
+ /// Внутренний идентификатор задачи на HwProj
+ Id,
+
+ /// Полное название назади
+ Title
+}
+
+public enum StudentIdType
+{
+ /// Внутренний идентификатор студента на HwProj
+ Id,
+
+ /// Полное имя студента
+ FullName,
+
+ /// Привязанный к HwProj логин студента на GitHub
+ GitHub
+}
diff --git a/HwProj.APIGateway/HwProj.APIGateway.API/Models/Solutions/SolutionPreviewView.cs b/HwProj.APIGateway/HwProj.APIGateway.API/Models/Solutions/SolutionPreviewView.cs
index 7944df290..4472e868c 100644
--- a/HwProj.APIGateway/HwProj.APIGateway.API/Models/Solutions/SolutionPreviewView.cs
+++ b/HwProj.APIGateway/HwProj.APIGateway.API/Models/Solutions/SolutionPreviewView.cs
@@ -1,26 +1,26 @@
using System;
using HwProj.Models.AuthService.DTO;
-namespace HwProj.APIGateway.API.Models.Solutions
+namespace HwProj.APIGateway.API.Models.Solutions;
+
+public class SolutionPreviewView
{
- public class SolutionPreviewView
- {
- public long SolutionId { get; set; }
- public AccountDataDto Student { get; set; }
- public string CourseTitle { get; set; }
- public long CourseId { get; set; }
- public string HomeworkTitle { get; set; }
- public string TaskTitle { get; set; }
- public long TaskId { get; set; }
- public DateTime PublicationDate { get; set; }
- public long? GroupId { get; set; }
- public bool IsFirstTry { get; set; }
- public bool SentAfterDeadline { get; set; }
- public bool IsCourseCompleted { get; set; }
- }
+ public long SolutionId { get; set; }
+ public AccountDataDto Student { get; set; }
+ public string CourseTitle { get; set; }
+ public long CourseId { get; set; }
+ public string HomeworkTitle { get; set; }
+ public string TaskTitle { get; set; }
+ public long TaskId { get; set; }
+ public DateTime PublicationDate { get; set; }
+ public long? GroupId { get; set; }
+ public bool IsFirstTry { get; set; }
+ public bool SentAfterDeadline { get; set; }
+ public bool IsCourseCompleted { get; set; }
+ public bool IsTest { get; set; }
+}
- public class UnratedSolutionPreviews
- {
- public SolutionPreviewView[] UnratedSolutions { get; set; }
- }
+public class UnratedSolutionPreviews
+{
+ public SolutionPreviewView[] UnratedSolutions { get; set; }
}
diff --git a/HwProj.APIGateway/HwProj.APIGateway.API/Models/Solutions/UserTaskSolutions.cs b/HwProj.APIGateway/HwProj.APIGateway.API/Models/Solutions/UserTaskSolutions.cs
index 668f585d7..7f79d2cd2 100644
--- a/HwProj.APIGateway/HwProj.APIGateway.API/Models/Solutions/UserTaskSolutions.cs
+++ b/HwProj.APIGateway/HwProj.APIGateway.API/Models/Solutions/UserTaskSolutions.cs
@@ -1,68 +1,67 @@
using HwProj.Models.AuthService.DTO;
using HwProj.Models.CoursesService.DTO;
-using HwProj.Models.CoursesService.ViewModels;
using HwProj.Models.SolutionsService;
using HwProj.Models.StatisticsService;
-namespace HwProj.APIGateway.API.Models.Solutions
-{
- public class UserTaskSolutions
- {
- public GetSolutionModel[] Solutions { get; set; }
- public StudentDataDto Student { get; set; }
- }
+namespace HwProj.APIGateway.API.Models.Solutions;
- public class UserTaskSolutions2
- {
- public int MaxRating { get; set; }
- public string Title { get; set; }
- public string[] Tags { get; set; }
- public string TaskId { get; set; }
- public GetSolutionModel[] Solutions { get; set; }
- }
+public class UserTaskSolutions
+{
+ public GetSolutionModel[] Solutions { get; set; }
+ public StudentDataDto Student { get; set; }
+ public bool HasDifferentReviewer { get; set; }
+}
- public class TaskSolutionStatisticsPageData
- {
- public TaskSolutions[] TaskSolutions { get; set; }
- public long CourseId { get; set; }
- public HomeworksGroupSolutionStats[] StatsForTasks { get; set; }
- }
+public class UserTaskSolutions2
+{
+ public int MaxRating { get; set; }
+ public string Title { get; set; }
+ public string[] Tags { get; set; }
+ public string TaskId { get; set; }
+ public GetSolutionModel[] Solutions { get; set; }
+}
- public class UserTaskSolutionsPageData
- {
- public long CourseId { get; set; }
- public AccountDataDto[] CourseMates { get; set; }
- public HomeworksGroupUserTaskSolutions[] TaskSolutions { get; set; }
- }
+public class TaskSolutionStatisticsPageData
+{
+ public AccountDataDto[] CourseMentors { get; set; }
+ public TaskSolutions[] TaskSolutions { get; set; }
+ public long CourseId { get; set; }
+ public HomeworksGroupSolutionStats[] StatsForTasks { get; set; }
+}
- public class HomeworksGroupUserTaskSolutions
- {
- public string? GroupTitle { get; set; }
- public HomeworkUserTaskSolutions[] HomeworkSolutions { get; set; }
- }
+public class UserTaskSolutionsPageData
+{
+ public long CourseId { get; set; }
+ public AccountDataDto[] CourseMates { get; set; }
+ public HomeworksGroupUserTaskSolutions[] TaskSolutions { get; set; }
+}
- public class HomeworkUserTaskSolutions
- {
- public string HomeworkTitle { get; set; }
- public UserTaskSolutions2[] StudentSolutions { get; set; }
- }
+public class HomeworksGroupUserTaskSolutions
+{
+ public string? GroupTitle { get; set; }
+ public HomeworkUserTaskSolutions[] HomeworkSolutions { get; set; }
+}
+public class HomeworkUserTaskSolutions
+{
+ public string HomeworkTitle { get; set; }
+ public UserTaskSolutions2[] StudentSolutions { get; set; }
+}
- public class TaskSolutions
- {
- public long TaskId { get; set; }
- public UserTaskSolutions[] StudentSolutions { get; set; }
- }
+public class TaskSolutions
+{
+ public long TaskId { get; set; }
+ public UserTaskSolutions[] StudentSolutions { get; set; }
+}
- public class HomeworksGroupSolutionStats
- {
- public string? GroupTitle { get; set; }
- public HomeworkSolutionsStats[] StatsForHomeworks { get; set; }
- }
+public class HomeworksGroupSolutionStats
+{
+ public string? GroupTitle { get; set; }
+ public HomeworkSolutionsStats[] StatsForHomeworks { get; set; }
+}
- public class HomeworkSolutionsStats
- {
- public string HomeworkTitle { get; set; }
- public TaskSolutionsStats[] StatsForTasks { get; set; }
- }
+public class HomeworkSolutionsStats
+{
+ public string HomeworkTitle { get; set; }
+ public TaskSolutionsStats[] StatsForTasks { get; set; }
}
diff --git a/HwProj.APIGateway/HwProj.APIGateway.API/Models/Statistics/AdvancedCourseStatisticsViewModel.cs b/HwProj.APIGateway/HwProj.APIGateway.API/Models/Statistics/AdvancedCourseStatisticsViewModel.cs
index ebecaa9b1..6984237d1 100644
--- a/HwProj.APIGateway/HwProj.APIGateway.API/Models/Statistics/AdvancedCourseStatisticsViewModel.cs
+++ b/HwProj.APIGateway/HwProj.APIGateway.API/Models/Statistics/AdvancedCourseStatisticsViewModel.cs
@@ -1,14 +1,13 @@
using HwProj.Models.CoursesService.ViewModels;
using HwProj.Models.StatisticsService;
-namespace HwProj.APIGateway.API.Models.Statistics
+namespace HwProj.APIGateway.API.Models.Statistics;
+
+public class AdvancedCourseStatisticsViewModel
{
- public class AdvancedCourseStatisticsViewModel
- {
- public CoursePreview Course { get; set; }
- public HomeworkViewModel[] Homeworks { get; set; }
- public StatisticsCourseMatesModel[] StudentStatistics { get; set; }
- public StatisticsCourseMeasureSolutionModel[] AverageStudentSolutions { get; set; }
- public StatisticsCourseMeasureSolutionModel[] BestStudentSolutions { get; set; }
- }
-}
\ No newline at end of file
+ public CoursePreview Course { get; set; }
+ public HomeworkViewModel[] Homeworks { get; set; }
+ public StatisticsCourseMatesModel[] StudentStatistics { get; set; }
+ public StatisticsCourseMeasureSolutionModel[] AverageStudentSolutions { get; set; }
+ public StatisticsCourseMeasureSolutionModel[] BestStudentSolutions { get; set; }
+}
diff --git a/HwProj.APIGateway/HwProj.APIGateway.API/Models/Statistics/StatisticsCourseMatesModel.cs b/HwProj.APIGateway/HwProj.APIGateway.API/Models/Statistics/StatisticsCourseMatesModel.cs
index f97403cf1..5bc6f4609 100644
--- a/HwProj.APIGateway/HwProj.APIGateway.API/Models/Statistics/StatisticsCourseMatesModel.cs
+++ b/HwProj.APIGateway/HwProj.APIGateway.API/Models/Statistics/StatisticsCourseMatesModel.cs
@@ -3,14 +3,13 @@
using HwProj.Models.AuthService.DTO;
using HwProj.Models.StatisticsService;
-namespace HwProj.APIGateway.API.Models.Statistics
+namespace HwProj.APIGateway.API.Models.Statistics;
+
+public class StatisticsCourseMatesModel
{
- public class StatisticsCourseMatesModel
- {
- public string Id { get; set; }
- public string Name { get; set; }
- public string Surname { get; set; }
- public AccountDataDto[] Reviewers { get; set; } = Array.Empty();
- public List Homeworks { get; set; } = new List();
- }
+ public string Id { get; set; }
+ public string Name { get; set; }
+ public string Surname { get; set; }
+ public AccountDataDto[] Reviewers { get; set; } = Array.Empty();
+ public List Homeworks { get; set; } = new();
}
diff --git a/HwProj.APIGateway/HwProj.APIGateway.API/Models/Statistics/StatisticsLecturersModel.cs b/HwProj.APIGateway/HwProj.APIGateway.API/Models/Statistics/StatisticsLecturersModel.cs
index c130d7092..4147c9280 100644
--- a/HwProj.APIGateway/HwProj.APIGateway.API/Models/Statistics/StatisticsLecturersModel.cs
+++ b/HwProj.APIGateway/HwProj.APIGateway.API/Models/Statistics/StatisticsLecturersModel.cs
@@ -1,11 +1,10 @@
using HwProj.Models.AuthService.DTO;
-namespace HwProj.APIGateway.API.Models.Statistics
+namespace HwProj.APIGateway.API.Models.Statistics;
+
+public class StatisticsLecturersModel
{
- public class StatisticsLecturersModel
- {
- public AccountDataDto Lecturer { get; set; }
- public int NumberOfCheckedSolutions { get; set; }
- public int NumberOfCheckedUniqueSolutions { get; set; }
- }
+ public AccountDataDto Lecturer { get; set; }
+ public int NumberOfCheckedSolutions { get; set; }
+ public int NumberOfCheckedUniqueSolutions { get; set; }
}
diff --git a/HwProj.APIGateway/HwProj.APIGateway.API/Models/SystemInfo.cs b/HwProj.APIGateway/HwProj.APIGateway.API/Models/SystemInfo.cs
index 56c142588..9d483c581 100644
--- a/HwProj.APIGateway/HwProj.APIGateway.API/Models/SystemInfo.cs
+++ b/HwProj.APIGateway/HwProj.APIGateway.API/Models/SystemInfo.cs
@@ -1,8 +1,7 @@
-namespace HwProj.APIGateway.API.Models
+namespace HwProj.APIGateway.API.Models;
+
+public class SystemInfo
{
- public class SystemInfo
- {
- public string Service { get; set; }
- public bool IsAvailable { get; set; }
- }
+ public string Service { get; set; }
+ public bool IsAvailable { get; set; }
}
diff --git a/HwProj.APIGateway/HwProj.APIGateway.API/Models/Tasks/TaskDeadlineView.cs b/HwProj.APIGateway/HwProj.APIGateway.API/Models/Tasks/TaskDeadlineView.cs
index 001342249..015ed4265 100644
--- a/HwProj.APIGateway/HwProj.APIGateway.API/Models/Tasks/TaskDeadlineView.cs
+++ b/HwProj.APIGateway/HwProj.APIGateway.API/Models/Tasks/TaskDeadlineView.cs
@@ -1,14 +1,13 @@
using HwProj.Models.CoursesService.DTO;
using HwProj.Models.SolutionsService;
-namespace HwProj.APIGateway.API.Models.Tasks
+namespace HwProj.APIGateway.API.Models.Tasks;
+
+public class TaskDeadlineView
{
- public class TaskDeadlineView
- {
- public TaskDeadlineDto Deadline { get; set; }
- public SolutionState? SolutionState { get; set; }
- public long? Rating { get; set; }
- public long MaxRating { get; set; }
- public bool DeadlinePast { get; set; }
- }
+ public TaskDeadlineDto Deadline { get; set; }
+ public SolutionState? SolutionState { get; set; }
+ public long? Rating { get; set; }
+ public long MaxRating { get; set; }
+ public bool DeadlinePast { get; set; }
}
diff --git a/HwProj.APIGateway/HwProj.APIGateway.API/Models/UserDataDto.cs b/HwProj.APIGateway/HwProj.APIGateway.API/Models/UserDataDto.cs
index f4af2a43c..bbd4cbfd0 100644
--- a/HwProj.APIGateway/HwProj.APIGateway.API/Models/UserDataDto.cs
+++ b/HwProj.APIGateway/HwProj.APIGateway.API/Models/UserDataDto.cs
@@ -1,12 +1,11 @@
using HwProj.APIGateway.API.Models.Tasks;
using HwProj.Models.AuthService.DTO;
-namespace HwProj.APIGateway.API.Models
+namespace HwProj.APIGateway.API.Models;
+
+public class UserDataDto
{
- public class UserDataDto
- {
- public AccountDataDto UserData { get; set; }
- public CourseEvents[] CourseEvents { get; set; }
- public TaskDeadlineView[] TaskDeadlines { get; set; }
- }
+ public AccountDataDto UserData { get; set; }
+ public CourseEvents[] CourseEvents { get; set; }
+ public TaskDeadlineView[] TaskDeadlines { get; set; }
}
diff --git a/HwProj.APIGateway/HwProj.APIGateway.API/Program.cs b/HwProj.APIGateway/HwProj.APIGateway.API/Program.cs
index 47a2138f8..71679216f 100644
--- a/HwProj.APIGateway/HwProj.APIGateway.API/Program.cs
+++ b/HwProj.APIGateway/HwProj.APIGateway.API/Program.cs
@@ -2,25 +2,21 @@
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
-namespace HwProj.APIGateway.API
+namespace HwProj.APIGateway.API;
+
+public static class Program
{
- public static class Program
+ public static void Main(string[] args)
{
- public static void Main(string[] args)
- {
- WebHost.CreateDefaultBuilder(args)
- .UseStartup()
- .ConfigureAppConfiguration((hostingContext, config) =>
- {
- config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath)
- .AddEnvironmentVariables();
- })
- .ConfigureKestrel(options =>
- {
- options.Limits.MaxRequestBodySize = 200 * 1024 * 1024;
- })
- .Build()
- .Run();
- }
+ WebHost.CreateDefaultBuilder(args)
+ .UseStartup()
+ .ConfigureAppConfiguration((hostingContext, config) =>
+ {
+ config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath)
+ .AddEnvironmentVariables();
+ })
+ .ConfigureKestrel(options => { options.Limits.MaxRequestBodySize = 200 * 1024 * 1024; })
+ .Build()
+ .Run();
}
}
diff --git a/HwProj.APIGateway/HwProj.APIGateway.API/Startup.cs b/HwProj.APIGateway/HwProj.APIGateway.API/Startup.cs
index cf053dec6..ef7c604ad 100644
--- a/HwProj.APIGateway/HwProj.APIGateway.API/Startup.cs
+++ b/HwProj.APIGateway/HwProj.APIGateway.API/Startup.cs
@@ -1,72 +1,144 @@
-using HwProj.AuthService.Client;
+using System.Collections.Generic;
+using System.Text;
+using System.Text.Json.Serialization;
+using HwProj.APIGateway.API.Filters;
+using HwProj.AuthService.Client;
using HwProj.ContentService.Client;
using HwProj.CoursesService.Client;
using HwProj.NotificationsService.Client;
using HwProj.SolutionsService.Client;
-using HwProj.Utils.Auth;
-using HwProj.Utils.Configuration;
-using HwProj.APIGateway.API.Filters;
+using IStudentsInfo;
+using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder;
-using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
using Microsoft.IdentityModel.Tokens;
-using IStudentsInfo;
+using Microsoft.OpenApi.Models;
using StudentsInfo;
-namespace HwProj.APIGateway.API
+namespace HwProj.APIGateway.API;
+
+public class Startup
{
- public class Startup
+ public Startup(IConfiguration configuration)
{
- public Startup(IConfiguration configuration)
- {
- Configuration = configuration;
- }
+ Configuration = configuration;
+ }
- public IConfiguration Configuration { get; }
+ public IConfiguration Configuration { get; }
- public void ConfigureServices(IServiceCollection services)
- {
- services.Configure(options => { options.MultipartBodyLengthLimit = 200 * 1024 * 1024; });
- services.ConfigureHwProjServices("API Gateway");
- services.AddSingleton(provider =>
- new StudentsInformationProvider(Configuration["StudentsInfo:Login"],
- Configuration["StudentsInfo:Password"],
- Configuration["StudentsInfo:LdapHost"], int.Parse(Configuration["StudentsInfo:LdapPort"]),
- Configuration["StudentsInfo:SearchBase"]));
- const string authenticationProviderKey = "GatewayKey";
+ public void ConfigureServices(IServiceCollection services)
+ {
+ services
+ .AddCors()
+ .AddControllers()
+ .AddJsonOptions(options =>
+ options.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.IgnoreCycles);
+
+ services.AddHttpContextAccessor();
+ services.AddAutoMapper(x => x.AddProfile());
+ services.Configure(options => { options.MultipartBodyLengthLimit = 200 * 1024 * 1024; });
+ ConfigureHwProjServiceSwaggerGen(services);
- services.AddAuthentication()
- .AddJwtBearer(authenticationProviderKey, x =>
+ services.AddSingleton(provider =>
+ new StudentsInformationProvider(Configuration["StudentsInfo:Login"],
+ Configuration["StudentsInfo:Password"],
+ Configuration["StudentsInfo:LdapHost"], int.Parse(Configuration["StudentsInfo:LdapPort"]),
+ Configuration["StudentsInfo:SearchBase"]));
+
+ var appSettings = Configuration.GetSection("Security");
+
+ services.AddAuthentication(options =>
+ {
+ options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
+ options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
+ })
+ .AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, x =>
+ {
+ x.RequireHttpsMetadata = false;
+ x.TokenValidationParameters = new TokenValidationParameters
{
- x.RequireHttpsMetadata = false;
- x.TokenValidationParameters = new TokenValidationParameters
- {
- ValidIssuer = "AuthService",
- ValidateIssuer = true,
- ValidateAudience = false,
- ValidateLifetime = true,
- IssuerSigningKey = AuthorizationKey.SecurityKey,
- ValidateIssuerSigningKey = true
- };
- });
+ ValidIssuer = "AuthService",
+ ValidateIssuer = true,
+ ValidateAudience = false,
+ ValidateLifetime = true,
+ IssuerSigningKey =
+ new SymmetricSecurityKey(Encoding.ASCII.GetBytes(appSettings["SecurityKey"])),
+ ValidateIssuerSigningKey = true
+ };
+ });
+
+ services.AddHttpClient();
+ services.AddHttpContextAccessor();
- services.AddHttpClient();
- services.AddHttpContextAccessor();
+ services.AddAuthServiceClient();
+ services.AddCoursesServiceClient();
+ services.AddSolutionServiceClient();
+ services.AddNotificationsServiceClient();
+ services.AddContentServiceClient();
- services.AddAuthServiceClient();
- services.AddCoursesServiceClient();
- services.AddSolutionServiceClient();
- services.AddNotificationsServiceClient();
- services.AddContentServiceClient();
+ services.AddScoped();
+ services.AddScoped();
+ services.AddScoped();
+ }
- services.AddScoped();
- }
+ public void Configure(IApplicationBuilder app, IHostEnvironment env)
+ {
+ if (env.IsDevelopment())
+ app.UseDeveloperExceptionPage()
+ .UseSwagger()
+ .UseSwaggerUI(c => { c.SwaggerEndpoint("/swagger/v1/swagger.json", "API Gateway"); });
+ else
+ app.UseHsts();
+
+ app.UseRouting();
+ app.UseAuthentication();
+ app.UseAuthorization();
+ app.UseCors(x => x
+ .AllowAnyMethod()
+ .AllowAnyHeader()
+ .SetIsOriginAllowed(_ => true)
+ .AllowCredentials());
+
+ app.UseEndpoints(x => x.MapControllers());
+ }
- public void Configure(IApplicationBuilder app, IHostingEnvironment env)
+ private static void ConfigureHwProjServiceSwaggerGen(IServiceCollection services)
+ {
+ services.AddSwaggerGen(c =>
{
- app.ConfigureHwProj(env, "API Gateway");
- }
+ c.SwaggerDoc("v1", new OpenApiInfo { Title = "API Gateway", Version = "v1" });
+ c.CustomOperationIds(apiDesc =>
+ {
+ var controllerName = apiDesc.ActionDescriptor.RouteValues["controller"];
+ var actionName = apiDesc.ActionDescriptor.RouteValues["action"];
+ return $"{controllerName}{actionName}";
+ });
+ c.AddSecurityDefinition("Bearer",
+ new OpenApiSecurityScheme
+ {
+ In = ParameterLocation.Header,
+ Description = "Please enter into field the word 'Bearer' following by space and JWT",
+ Name = "Authorization",
+ Type = SecuritySchemeType.ApiKey
+ });
+ c.AddSecurityRequirement(
+ new OpenApiSecurityRequirement
+ {
+ {
+ new OpenApiSecurityScheme
+ {
+ Reference = new OpenApiReference
+ {
+ Id = "Bearer",
+ Type = ReferenceType.SecurityScheme
+ }
+ },
+ new List()
+ }
+ });
+ });
}
}
diff --git a/HwProj.APIGateway/HwProj.APIGateway.API/appsettings.json b/HwProj.APIGateway/HwProj.APIGateway.API/appsettings.json
index 92453a812..b3e146a09 100644
--- a/HwProj.APIGateway/HwProj.APIGateway.API/appsettings.json
+++ b/HwProj.APIGateway/HwProj.APIGateway.API/appsettings.json
@@ -5,7 +5,10 @@
"Notifications": "http://localhost:5006",
"Solutions": "http://localhost:5007",
"Content": "http://localhost:5008"
- },
+ },
+ "Security": {
+ "SecurityKey": "U8_.wpvk93fPWG InviteNewLecturer(InviteLecturerViewModel model
}
[HttpGet("findByEmail/{email}")]
- [ProducesResponseType(typeof(User), (int)HttpStatusCode.OK)]
+ [ProducesResponseType(typeof(AccountDataDto), (int)HttpStatusCode.OK)]
public async Task FindByEmail(string email)
{
var user = await _userManager.FindByEmailAsync(email);
- return Ok(user);
- }
-
- [HttpGet("getRole")]
- [ProducesResponseType(typeof(string), (int)HttpStatusCode.OK)]
- public async Task GetRolesAsync([FromBody] User user)
- {
var roles = await _userManager.GetRolesAsync(user);
- return Ok(roles[0]);
+ return Ok(user.ToAccountDataDto(roles.First()));
}
[HttpGet("getAllStudents")]
@@ -141,7 +134,7 @@ public async Task GetAllStudents()
public async Task GetAllLecturers()
{
var allLecturers = await _accountService.GetUsersInRole(Roles.LecturerRole);
- var result = allLecturers.ToArray();
+ var result = allLecturers.Select(x => x.ToAccountDataDto(Roles.LecturerRole)).ToArray();
return Ok(result);
}
diff --git a/HwProj.AuthService/HwProj.AuthService.API/Dockerfile b/HwProj.AuthService/HwProj.AuthService.API/Dockerfile
index 22da9c224..a54124dd0 100644
--- a/HwProj.AuthService/HwProj.AuthService.API/Dockerfile
+++ b/HwProj.AuthService/HwProj.AuthService.API/Dockerfile
@@ -1,18 +1,20 @@
-FROM mcr.microsoft.com/dotnet/core/sdk:2.2-stretch AS build
+FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /src
COPY ["Directory.Build.props", "Directory.Build.props"]
COPY ["HwProj.AuthService/HwProj.AuthService.API/", "HwProj.AuthService/HwProj.AuthService.API/"]
-COPY ["HwProj.Common/HwProj.Utils/", "HwProj.Common/HwProj.Utils/"]
-COPY ["HwProj.EventBus/HwProj.EventBus.Client/", "HwProj.EventBus/HwProj.EventBus.Client/"]
+COPY ["HwProj.Common/HwProj.Common.Net8/", "HwProj.Common/HwProj.Common.Net8/"]
COPY ["HwProj.Common/HwProj.Models/", "HwProj.Common/HwProj.Models/"]
COPY ["HwProj.Common/HwProj.Repositories/", "HwProj.Common/HwProj.Repositories/"]
+COPY ["HwProj.Common/HwProj.Repositories.Net8/", "HwProj.Common/HwProj.Repositories.Net8/"]
+COPY ["HwProj.EventBus/HwProj.EventBus.Client/", "HwProj.EventBus/HwProj.EventBus.Client/"]
+COPY ["HwProj.NotificationsService/HwProj.NotificationService.Events/", "HwProj.NotificationsService/HwProj.NotificationService.Events/"]
WORKDIR "/src/HwProj.AuthService/HwProj.AuthService.API"
RUN dotnet publish "HwProj.AuthService.API.csproj" -c Release -o /app/publish
-FROM mcr.microsoft.com/dotnet/core/aspnet:2.2-stretch-slim AS final
+FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS final
WORKDIR /app
COPY --from=build /app/publish .
diff --git a/HwProj.AuthService/HwProj.AuthService.API/Extensions/MappingExtensions.cs b/HwProj.AuthService/HwProj.AuthService.API/Extensions/MappingExtensions.cs
new file mode 100644
index 000000000..b9fb1d328
--- /dev/null
+++ b/HwProj.AuthService/HwProj.AuthService.API/Extensions/MappingExtensions.cs
@@ -0,0 +1,23 @@
+using HwProj.AuthService.API.Models;
+using HwProj.Models.AuthService.DTO;
+
+namespace HwProj.AuthService.API.Extensions
+{
+ public static class MappingExtensions
+ {
+ public static AccountDataDto ToAccountDataDto(this User user, string role)
+ {
+ return new AccountDataDto(
+ user.Id,
+ user.Name,
+ user.Surname,
+ user.Email,
+ role,
+ user.IsExternalAuth,
+ user.MiddleName,
+ user.GitHubId,
+ user.CompanyName,
+ user.Bio);
+ }
+ }
+}
diff --git a/HwProj.AuthService/HwProj.AuthService.API/HwProj.AuthService.API.csproj b/HwProj.AuthService/HwProj.AuthService.API/HwProj.AuthService.API.csproj
index ee29e95ca..1eaa0766d 100644
--- a/HwProj.AuthService/HwProj.AuthService.API/HwProj.AuthService.API.csproj
+++ b/HwProj.AuthService/HwProj.AuthService.API/HwProj.AuthService.API.csproj
@@ -1,34 +1,29 @@
- netcoreapp2.2
+ net8.0
Linux
..\..\docker-compose.dcproj
..\..
$(CSharpLanguageVersion)
603911e4-ace8-4439-96f8-1705a0dae761
- $(NullableReferenceTypes)
+ disable
-
-
-
-
-
-
-
+
+
+
+
+
-
+
-
-
-
-
+
diff --git a/HwProj.AuthService/HwProj.AuthService.API/Models/ExpertData.cs b/HwProj.AuthService/HwProj.AuthService.API/Models/ExpertData.cs
index b54695d9c..c95983f08 100644
--- a/HwProj.AuthService/HwProj.AuthService.API/Models/ExpertData.cs
+++ b/HwProj.AuthService/HwProj.AuthService.API/Models/ExpertData.cs
@@ -1,7 +1,6 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
-using HwProj.Models.AuthService.ViewModels;
-using HwProj.Repositories;
+using HwProj.Repositories.Net8;
namespace HwProj.AuthService.API.Models
{
diff --git a/HwProj.AuthService/HwProj.AuthService.API/Models/IdentityContext.cs b/HwProj.AuthService/HwProj.AuthService.API/Models/IdentityContext.cs
index 6991c6bfc..564a283e1 100644
--- a/HwProj.AuthService/HwProj.AuthService.API/Models/IdentityContext.cs
+++ b/HwProj.AuthService/HwProj.AuthService.API/Models/IdentityContext.cs
@@ -1,5 +1,4 @@
-using HwProj.Models.AuthService.ViewModels;
-using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
+using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
namespace HwProj.AuthService.API.Models
diff --git a/HwProj.AuthService/HwProj.AuthService.API/Models/User.cs b/HwProj.AuthService/HwProj.AuthService.API/Models/User.cs
new file mode 100644
index 000000000..e2058f1d1
--- /dev/null
+++ b/HwProj.AuthService/HwProj.AuthService.API/Models/User.cs
@@ -0,0 +1,25 @@
+using Microsoft.AspNetCore.Identity;
+
+namespace HwProj.AuthService.API.Models
+{
+ public class User : IdentityUser
+ {
+ public string GitHubId { get; set; }
+
+ public string Name { get; set; }
+
+ public string Surname { get; set; }
+
+ public string MiddleName { get; set; }
+
+ public bool IsExternalAuth { get; set; }
+
+ public string Bio { get; set; }
+
+ public string CompanyName { get; set; }
+
+ public User()
+ {
+ }
+ }
+}
diff --git a/HwProj.AuthService/HwProj.AuthService.API/Repositories/ExpertsRepository.cs b/HwProj.AuthService/HwProj.AuthService.API/Repositories/ExpertsRepository.cs
index 1470ae60e..bb6059771 100644
--- a/HwProj.AuthService/HwProj.AuthService.API/Repositories/ExpertsRepository.cs
+++ b/HwProj.AuthService/HwProj.AuthService.API/Repositories/ExpertsRepository.cs
@@ -1,9 +1,7 @@
using System.Linq;
using System.Threading.Tasks;
using HwProj.AuthService.API.Models;
-using HwProj.Models.AuthService.ViewModels;
-using HwProj.Repositories;
-using Microsoft.AspNetCore.Identity;
+using HwProj.Repositories.Net8;
using Microsoft.EntityFrameworkCore;
namespace HwProj.AuthService.API.Repositories
diff --git a/HwProj.AuthService/HwProj.AuthService.API/Repositories/IExpertsRepository.cs b/HwProj.AuthService/HwProj.AuthService.API/Repositories/IExpertsRepository.cs
index b619f530a..f16ea98b5 100644
--- a/HwProj.AuthService/HwProj.AuthService.API/Repositories/IExpertsRepository.cs
+++ b/HwProj.AuthService/HwProj.AuthService.API/Repositories/IExpertsRepository.cs
@@ -1,8 +1,6 @@
using System.Threading.Tasks;
using HwProj.AuthService.API.Models;
-using HwProj.Models.AuthService.DTO;
-using HwProj.Models.AuthService.ViewModels;
-using HwProj.Repositories;
+using HwProj.Repositories.Net8;
namespace HwProj.AuthService.API.Repositories
{
diff --git a/HwProj.AuthService/HwProj.AuthService.API/RoleInitializer.cs b/HwProj.AuthService/HwProj.AuthService.API/RoleInitializer.cs
index 8fd2e8ea2..f8cb0f20d 100644
--- a/HwProj.AuthService/HwProj.AuthService.API/RoleInitializer.cs
+++ b/HwProj.AuthService/HwProj.AuthService.API/RoleInitializer.cs
@@ -1,29 +1,33 @@
using Microsoft.AspNetCore.Identity;
using System.Threading.Tasks;
-using HwProj.AuthService.API.Events;
using HwProj.EventBus.Client.Interfaces;
using HwProj.Models.Roles;
-using HwProj.Models.AuthService.ViewModels;
+using HwProj.NotificationService.Events.AuthService;
+using User = HwProj.AuthService.API.Models.User;
namespace HwProj.AuthService.API
{
public class RoleInitializer
{
+ private static IdentityRole _lecturer = new IdentityRole(Roles.LecturerRole);
+ private static IdentityRole _student = new IdentityRole(Roles.StudentRole);
+ private static IdentityRole _expert = new IdentityRole(Roles.ExpertRole);
+
public static async Task InitializeAsync(UserManager userManager, RoleManager roleManager, IEventBus eventBus)
{
if(await roleManager.FindByNameAsync(Roles.LecturerRole) == null)
{
- await roleManager.CreateAsync(Roles.Lecturer);
+ await roleManager.CreateAsync(_lecturer);
}
if (await roleManager.FindByNameAsync(Roles.StudentRole) == null)
{
- await roleManager.CreateAsync(Roles.Student);
+ await roleManager.CreateAsync(_student);
}
if (await roleManager.FindByNameAsync(Roles.ExpertRole) == null)
{
- await roleManager.CreateAsync(Roles.Expert);
+ await roleManager.CreateAsync(_expert);
}
const string email = "admin@gmail.com";
diff --git a/HwProj.AuthService/HwProj.AuthService.API/Services/AccountService.cs b/HwProj.AuthService/HwProj.AuthService.API/Services/AccountService.cs
index 945e48e58..706886b30 100644
--- a/HwProj.AuthService/HwProj.AuthService.API/Services/AccountService.cs
+++ b/HwProj.AuthService/HwProj.AuthService.API/Services/AccountService.cs
@@ -8,17 +8,15 @@
using AutoMapper;
using HwProj.AuthService.API.Extensions;
using HwProj.Models.Roles;
-using HwProj.AuthService.API.Events;
-using HwProj.AuthService.API.Repositories;
using HwProj.EventBus.Client.Interfaces;
using HwProj.Models.AuthService.DTO;
using HwProj.Models.AuthService.ViewModels;
using HwProj.Models.Result;
-using HwProj.Utils.Authorization;
+using HwProj.NotificationService.Events.AuthService;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Octokit;
-using User = HwProj.Models.AuthService.ViewModels.User;
+using User = HwProj.AuthService.API.Models.User;
namespace HwProj.AuthService.API.Services
@@ -95,6 +93,11 @@ public async Task GetAccountDataByEmailAsync(string email)
public async Task> RegisterUserAsync(RegisterDataDTO model)
{
+ model.Email = model.Email.Trim();
+ model.Name = model.Name.Trim();
+ model.Surname = model.Surname.Trim();
+ model.MiddleName = model.MiddleName.Trim();
+
if (await _userManager.FindByEmailAsync(model.Email) != null)
return Result.Failed("Пользователь уже зарегистрирован");
diff --git a/HwProj.AuthService/HwProj.AuthService.API/Services/AuthTokenService.cs b/HwProj.AuthService/HwProj.AuthService.API/Services/AuthTokenService.cs
index 3c5ea76a5..528a019ab 100644
--- a/HwProj.AuthService/HwProj.AuthService.API/Services/AuthTokenService.cs
+++ b/HwProj.AuthService/HwProj.AuthService.API/Services/AuthTokenService.cs
@@ -10,8 +10,8 @@
using HwProj.Models.Roles;
using Microsoft.Extensions.Configuration;
using HwProj.Models.AuthService.DTO;
-using HwProj.Models.AuthService.ViewModels;
using HwProj.Models.Result;
+using User = HwProj.AuthService.API.Models.User;
namespace HwProj.AuthService.API.Services
{
diff --git a/HwProj.AuthService/HwProj.AuthService.API/Services/ExpertsService.cs b/HwProj.AuthService/HwProj.AuthService.API/Services/ExpertsService.cs
index b17d402e0..bc48b14b2 100644
--- a/HwProj.AuthService/HwProj.AuthService.API/Services/ExpertsService.cs
+++ b/HwProj.AuthService/HwProj.AuthService.API/Services/ExpertsService.cs
@@ -9,7 +9,7 @@
using HwProj.Models.Result;
using HwProj.Models.Roles;
using Microsoft.AspNetCore.Identity;
-using Microsoft.EntityFrameworkCore.Internal;
+using User = HwProj.AuthService.API.Models.User;
namespace HwProj.AuthService.API.Services
{
@@ -68,7 +68,7 @@ await _expertsRepository.AddAsync(new ExpertData
Id = user.Id,
LecturerId = lecturerId,
IsProfileEdited = false,
- Tags = model.Tags.Join(";")
+ Tags = string.Join(';', model.Tags)
});
return Result.Success();
@@ -171,7 +171,7 @@ public async Task UpdateExpertTags(string lecturerId, UpdateExpertTagsDT
await _expertsRepository.UpdateAsync(updateExpertTagsDto.ExpertId, data => new ExpertData()
{
- Tags = updateExpertTagsDto.Tags.Join(";")
+ Tags = string.Join(';', updateExpertTagsDto.Tags)
});
return Result.Success();
diff --git a/HwProj.AuthService/HwProj.AuthService.API/Services/IAccountService.cs b/HwProj.AuthService/HwProj.AuthService.API/Services/IAccountService.cs
index 463161087..ca7d3c1d4 100644
--- a/HwProj.AuthService/HwProj.AuthService.API/Services/IAccountService.cs
+++ b/HwProj.AuthService/HwProj.AuthService.API/Services/IAccountService.cs
@@ -4,6 +4,7 @@
using HwProj.Models.AuthService.DTO;
using HwProj.Models.AuthService.ViewModels;
using HwProj.Models.Result;
+using User = HwProj.AuthService.API.Models.User;
namespace HwProj.AuthService.API.Services
{
diff --git a/HwProj.AuthService/HwProj.AuthService.API/Services/IAuthTokenService.cs b/HwProj.AuthService/HwProj.AuthService.API/Services/IAuthTokenService.cs
index 18ed24100..9dc2b45fc 100644
--- a/HwProj.AuthService/HwProj.AuthService.API/Services/IAuthTokenService.cs
+++ b/HwProj.AuthService/HwProj.AuthService.API/Services/IAuthTokenService.cs
@@ -1,7 +1,7 @@
using HwProj.Models.AuthService.DTO;
-using HwProj.Models.AuthService.ViewModels;
using System.Threading.Tasks;
using HwProj.Models.Result;
+using User = HwProj.AuthService.API.Models.User;
namespace HwProj.AuthService.API.Services
{
diff --git a/HwProj.AuthService/HwProj.AuthService.API/Services/IUserManager.cs b/HwProj.AuthService/HwProj.AuthService.API/Services/IUserManager.cs
index 9ce9db520..f041a232b 100644
--- a/HwProj.AuthService/HwProj.AuthService.API/Services/IUserManager.cs
+++ b/HwProj.AuthService/HwProj.AuthService.API/Services/IUserManager.cs
@@ -1,7 +1,7 @@
using System.Collections.Generic;
using System.Threading.Tasks;
-using HwProj.Models.AuthService.ViewModels;
using Microsoft.AspNetCore.Identity;
+using User = HwProj.AuthService.API.Models.User;
namespace HwProj.AuthService.API.Services
{
diff --git a/HwProj.AuthService/HwProj.AuthService.API/Services/ProxyUserManager.cs b/HwProj.AuthService/HwProj.AuthService.API/Services/ProxyUserManager.cs
index 533b149cb..593a7e07e 100644
--- a/HwProj.AuthService/HwProj.AuthService.API/Services/ProxyUserManager.cs
+++ b/HwProj.AuthService/HwProj.AuthService.API/Services/ProxyUserManager.cs
@@ -1,7 +1,7 @@
using System.Collections.Generic;
using System.Threading.Tasks;
-using HwProj.Models.AuthService.ViewModels;
using Microsoft.AspNetCore.Identity;
+using User = HwProj.AuthService.API.Models.User;
namespace HwProj.AuthService.API.Services
{
diff --git a/HwProj.AuthService/HwProj.AuthService.API/Startup.cs b/HwProj.AuthService/HwProj.AuthService.API/Startup.cs
index 07990e8ff..47cd285cd 100644
--- a/HwProj.AuthService/HwProj.AuthService.API/Startup.cs
+++ b/HwProj.AuthService/HwProj.AuthService.API/Startup.cs
@@ -1,5 +1,5 @@
-using Microsoft.AspNetCore.Builder;
-using Microsoft.AspNetCore.Hosting;
+using System.Text.Json.Serialization;
+using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using HwProj.AuthService.API.Models;
@@ -7,13 +7,11 @@
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using HwProj.AuthService.API.Services;
+using HwProj.Common.Net8;
+using HwProj.EventBus.Client;
using HwProj.EventBus.Client.Interfaces;
-using Microsoft.IdentityModel.Tokens;
-using Microsoft.AspNetCore.Authentication.JwtBearer;
-using HwProj.Utils.Configuration;
-using HwProj.Models.AuthService.ViewModels;
-using HwProj.Models.Roles;
-using HwProj.Utils.Auth;
+using Microsoft.Extensions.Hosting;
+using User = HwProj.AuthService.API.Models.User;
namespace HwProj.AuthService.API
{
@@ -28,26 +26,13 @@ public Startup(IConfiguration configuration)
public void ConfigureServices(IServiceCollection services)
{
- services.ConfigureHwProjServices("AuthService API");
-
- //var appSettingsSection = Configuration.GetSection("AppSettings");
- //services.Configure(appSettingsSection);
-
- services.AddAuthentication(options => { options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme; })
- .AddJwtBearer(x =>
- {
- x.RequireHttpsMetadata = false; //TODO: dev env setting
- x.TokenValidationParameters = new TokenValidationParameters
- {
- ValidIssuer = "AuthService",
- ValidateIssuer = true,
- ValidateAudience = false,
- ValidateLifetime = true,
- IssuerSigningKey = AuthorizationKey.SecurityKey,
- ValidateIssuerSigningKey = true
- };
- });
+ services
+ .AddCors()
+ .AddControllers()
+ .AddJsonOptions(options =>
+ options.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.IgnoreCycles);
+ services.AddAutoMapper(x => x.AddProfile());
services.AddHttpClient();
var connectionString = ConnectionString.GetConnectionString(Configuration);
@@ -77,22 +62,32 @@ public void ConfigureServices(IServiceCollection services)
.AddScoped();
}
- public void Configure(IApplicationBuilder app, IHostingEnvironment env, IdentityContext context)
+ public void Configure(IApplicationBuilder app, IHostEnvironment env, IdentityContext context)
{
- app.ConfigureHwProj(env, "AuthService API", context);
+ if (env.IsDevelopment()) app.UseDeveloperExceptionPage();
+ else app.UseHsts();
- using (var scope = app.ApplicationServices.CreateScope())
- {
- var userManager = scope.ServiceProvider.GetService(typeof(UserManager)) as UserManager;
+ app.UseRouting();
+ app.UseAuthentication();
+ app.UseCors(x => x
+ .AllowAnyMethod()
+ .AllowAnyHeader()
+ .SetIsOriginAllowed(_ => true)
+ .AllowCredentials());
- var rolesManager =
- scope.ServiceProvider.GetService(typeof(RoleManager)) as RoleManager;
- var eventBus = scope.ServiceProvider.GetService();
+ app.UseEndpoints(x => x.MapControllers());
- if (env.IsDevelopment())
- {
- RoleInitializer.InitializeAsync(userManager, rolesManager, eventBus).Wait();
- }
+ app.UseDatabase(env, context);
+
+ using var scope = app.ApplicationServices.CreateScope();
+ var userManager = scope.ServiceProvider.GetService>();
+
+ var rolesManager = scope.ServiceProvider.GetService>();
+ var eventBus = scope.ServiceProvider.GetService();
+
+ if (env.IsDevelopment())
+ {
+ RoleInitializer.InitializeAsync(userManager, rolesManager, eventBus).Wait();
}
}
}
diff --git a/HwProj.AuthService/HwProj.AuthService.API/appsettings.json b/HwProj.AuthService/HwProj.AuthService.API/appsettings.json
index 34f0e2986..bb18739ae 100644
--- a/HwProj.AuthService/HwProj.AuthService.API/appsettings.json
+++ b/HwProj.AuthService/HwProj.AuthService.API/appsettings.json
@@ -1,6 +1,6 @@
{
"ConnectionStrings": {
- "DefaultConnectionForWindows": "Server=(localdb)\\mssqllocaldb;Database=AuthServiceDB;Trusted_Connection=True;",
+ "DefaultConnectionForWindows": "Server=(localdb)\\mssqllocaldb;Database=AuthServiceDB;Trusted_Connection=True;TrustServerCertificate=true;",
"DefaultConnectionForLinux": "Server=localhost,1433;Database=AuthServiceDB;User ID=SA;Password=password_1234;"
},
"Logging": {
diff --git a/HwProj.AuthService/HwProj.AuthService.Client/AuthServiceClient.cs b/HwProj.AuthService/HwProj.AuthService.Client/AuthServiceClient.cs
index 24652becb..bf2d29112 100644
--- a/HwProj.AuthService/HwProj.AuthService.Client/AuthServiceClient.cs
+++ b/HwProj.AuthService/HwProj.AuthService.Client/AuthServiceClient.cs
@@ -163,8 +163,8 @@ public async Task FindByEmailAsync(string email)
};
var response = await _httpClient.SendAsync(httpRequest);
- var user = await response.DeserializeAsync();
- return user?.Id;
+ var user = await response.DeserializeAsync();
+ return user?.UserId;
}
public async Task