├── .vscode ├── extensions.json ├── settings.json └── tasks.json ├── ShareBook ├── ShareBook.Test.Integration │ ├── GlobalUsings.cs │ ├── Setup │ │ ├── ShareBookTestsFixture.cs │ │ └── ShareBookWebAppFactory.cs │ ├── ShareBook.Test.Integration.csproj │ └── Tests │ │ ├── BookTests │ │ └── BookTests.cs │ │ └── CategoryTests │ │ └── CategoryTests.cs ├── ShareBook.Api │ ├── wwwroot │ │ └── Images │ │ │ └── Books │ │ │ ├── os-sete.jpg │ │ │ ├── a-cabana.jpg │ │ │ ├── a-passagem.jpg │ │ │ ├── fantasia-1.jpg │ │ │ ├── o-cortico.jpg │ │ │ ├── star-wars.jpg │ │ │ ├── the-hobbit.jpg │ │ │ ├── guerra-civil.jpg │ │ │ ├── homem-aranha.jpg │ │ │ ├── nunca-jamais.jpg │ │ │ ├── as-duas-torres.jpg │ │ │ ├── os-vingadores.jpg │ │ │ ├── a-furia-dos-reis.jpg │ │ │ ├── a-hora-do-vampiro.jpg │ │ │ ├── anjos-e-demonios.jpg │ │ │ ├── antologia-poetica.jpg │ │ │ ├── direito-e-justiça.jpg │ │ │ ├── o-retorno-do-rei.png │ │ │ ├── se-venden-gorras.jpg │ │ │ ├── senhor-dos-aneis.jpg │ │ │ ├── the-book-of-jonah.jpg │ │ │ ├── a-irmandade-do-anel.jpg │ │ │ ├── a-maldicao-do-cigano.jpg │ │ │ ├── cronicas-gelo-e-fogo.jpg │ │ │ ├── direito-penal-rideel.jpg │ │ │ ├── num-vento-diferente.jpg │ │ │ ├── o-festim-dos-corvos.jpg │ │ │ ├── o-pequeno-principe.jpg │ │ │ ├── as-provacoes-de-apolo.jpg │ │ │ ├── o-segredo-das-sombras.jpg │ │ │ ├── orgulho-e-preconceito.jpg │ │ │ ├── programando-o-android.jpg │ │ │ ├── introducao-a-programacao.jpg │ │ │ ├── novelas-nada-exemplares.jpg │ │ │ ├── volta-ao-mundo-em-80-dias.jpg │ │ │ ├── a-menina-que-roubava-livros.jpg │ │ │ ├── c-sharp-programao-em-camada.jpg │ │ │ ├── programacao-de-jogo-android.jpg │ │ │ ├── percy-jackson-e-os-olimpianos.jpg │ │ │ ├── teoria-discursiva-do-direito.jpg │ │ │ ├── 100-segredos-das-pessoas-felizes.jpg │ │ │ ├── programacao-de-redes-com-python.jpg │ │ │ ├── the-hobbit-there-and-back-again.jpg │ │ │ ├── harry-potter-prisioneiro-de-askaban.jpg │ │ │ └── harry-potter-e-as-reliquias-da-morte.jpg │ ├── ViewModels │ │ ├── ParentAprovalVM.cs │ │ ├── TrackinNumberBookVM.cs │ │ ├── RequestBookVM.cs │ │ ├── DonateBookUserVM.cs │ │ ├── ChangePasswordUserVM.cs │ │ ├── ForgotMyPasswordVM.cs │ │ ├── AddFaciclitatorNotesVM.cs │ │ ├── ChangeUserPasswordByEmailAndHashCodeVM.cs │ │ ├── AccessHistoryVM.cs │ │ ├── BaseViewModel.cs │ │ ├── ApproveBookVM.cs │ │ ├── LoginUserVM.cs │ │ ├── CancelBookDonationVM.cs │ │ ├── EmailTestVM.cs │ │ ├── ContactUsVM.cs │ │ ├── NotificationOnesignalVM.cs │ │ ├── UpdateUserVM.cs │ │ ├── RequestersListVM.cs │ │ ├── CustomValidators │ │ │ └── StringLengthRangeOptionalAttribute.cs │ │ ├── CreateBookVM.cs │ │ ├── MyBooksRequestsVM.cs │ │ ├── UpdateBookVM.cs │ │ └── UserVM.cs │ ├── .config │ │ └── dotnet-tools.json │ ├── Properties │ │ └── launchSettings.json │ ├── Filters │ │ ├── GetClaimsFilter.cs │ │ ├── ValidateModelStateFilterAttribute.cs │ │ ├── AuthorizationFilter.cs │ │ └── ThrottleFilter.cs │ ├── Program.cs │ ├── Controllers │ │ ├── CategoryController.cs │ │ ├── ContactUsController.cs │ │ ├── Generic │ │ │ ├── BaseDeleteController.cs │ │ │ └── BaseController.cs │ │ └── MeetupController.cs │ ├── web.config │ ├── Services │ │ └── RollbarConfigurator.cs │ ├── appsettings.json │ └── AutoMapper │ │ └── ViewModelToDomainMappingProfile.cs ├── ShareBook.Domain │ ├── Enums │ │ ├── Profile.cs │ │ ├── TypeSegments.cs │ │ ├── JobResult.cs │ │ ├── JobInterval.cs │ │ ├── VisitorProfile.cs │ │ ├── BookType.cs │ │ ├── DonationStatus.cs │ │ ├── FreightOption.cs │ │ └── BookStatus.cs │ ├── Common │ │ ├── IIdProperty.cs │ │ ├── BaseEntity.cs │ │ ├── JobExecutorResult.cs │ │ ├── PagedList.cs │ │ └── Result.cs │ ├── Category.cs │ ├── DTOs │ │ ├── BookStatsDTO.cs │ │ ├── BookCancelationDTO.cs │ │ ├── UserAnonymizeDTO.cs │ │ ├── UserStatsDTO.cs │ │ └── RegisterUserDTO.cs │ ├── Exceptions │ │ ├── AwsSqsDisabledException.cs │ │ ├── MeetupDisabledException.cs │ │ └── SharebookException.cs │ ├── ContactUs.cs │ ├── LogEntry.cs │ ├── MeetupParticipant.cs │ ├── Validators │ │ ├── CategoryValidator.cs │ │ ├── AccessHistoryValidator.cs │ │ ├── MeetupValidator.cs │ │ ├── BookUserValidator.cs │ │ └── ContactUsValidator.cs │ ├── ShareBook.Domain.csproj │ ├── NotificationOnesignal.cs │ ├── Address.cs │ ├── Meetup.cs │ ├── JobHistory.cs │ ├── BookUser.cs │ ├── AccessHistory.cs │ └── MailBounce.cs ├── ShareBook.Service │ ├── Meetup │ │ ├── MeetupSettings.cs │ │ ├── IMeetupService.cs │ │ └── Dto │ │ │ ├── YoutubeDTOError.cs │ │ │ ├── YoutubeDetailDTO.cs │ │ │ ├── SymplaDTO.cs │ │ │ ├── MeetupParticipantDTO.cs │ │ │ └── YoutubeDTO.cs │ ├── Category │ │ ├── ICategoryService.cs │ │ └── CategoryService.cs │ ├── Server │ │ └── ServerSettings.cs │ ├── Email │ │ ├── IEmailTemplate.cs │ │ ├── EmailSettings.cs │ │ ├── Templates │ │ │ ├── ForgotPasswordTemplate.html │ │ │ ├── BookCanceledTemplate.html │ │ │ ├── AnonymizeNotifyAdms.html │ │ │ ├── ParentAprovedNotifyUser.html │ │ │ ├── WaitingApprovalTemplate.html │ │ │ ├── BookDonatedTemplate.html │ │ │ ├── BookApprovedTemplate.html │ │ │ ├── BookNoticeDonorTemplate.html │ │ │ ├── RequestParentAproval.html │ │ │ ├── BookReceivedTemplate.html │ │ │ ├── BookNoticeInterestedTemplate.html │ │ │ ├── BookCanceledNoticeUsersTemplate.html │ │ │ ├── LateDonationNotification.html │ │ │ ├── BookNoticeDeclinedUsersTemplate.html │ │ │ ├── BookTrackingNumberNoticeWinnerTemplate.html │ │ │ ├── NewBookInsertedTemplate.html │ │ │ ├── ChooseDateReminderTemplate.html │ │ │ └── ChooseDateRenewTemplate.html │ │ ├── IEmailService.cs │ │ └── EmailTemplate.cs │ ├── Recaptcha │ │ ├── IRecaptchaService.cs │ │ └── RecaptchaService.cs │ ├── Lgpd │ │ └── ILgpdService.cs │ ├── ContactUs │ │ ├── IContactUsEmailService.cs │ │ ├── IContactUsService.cs │ │ ├── ContactUsService.cs │ │ └── ContactUsEmailService.cs │ ├── Upload │ │ ├── ImageSettings.cs │ │ ├── IUploadService.cs │ │ └── UploadService.cs │ ├── AccessHistory │ │ ├── IAccessHistoryService.cs │ │ └── AccessHistoryService.cs │ ├── AwsSqs │ │ ├── Dto │ │ │ ├── SharebookMessage.cs │ │ │ ├── NewBookBody.cs │ │ │ └── MailSenderBody.cs │ │ ├── MailSenderLowPriorityQueue.cs │ │ └── MailSenderHighPriorityQueue .cs │ ├── AWSSQS │ │ ├── IAwsSqsQueue.cs │ │ ├── NewBookQueue.cs │ │ └── AWSSQSSettings.cs │ ├── Book │ │ ├── IBooksEmailService.cs │ │ └── IBookService.cs │ ├── Muambator │ │ ├── MuambatorDTO.cs │ │ ├── IMuambatorService.cs │ │ └── MuambatorConfigurator.cs │ ├── PushNotification │ │ ├── PushNotificationSettings.cs │ │ └── IPushNotificationService.cs │ ├── User │ │ ├── IUserEmailService.cs │ │ └── IUserService.cs │ ├── Authorization │ │ └── Permission.cs │ └── BookUser │ │ ├── IBookUsersEmailService.cs │ │ └── IBookUserService.cs ├── ShareBook.Repository │ ├── Repository │ │ ├── Book │ │ │ ├── IBookRepository.cs │ │ │ └── BookRepository.cs │ │ ├── Meetup │ │ │ ├── IMeetupRepository.cs │ │ │ ├── IMeetupParticipantRepository.cs │ │ │ ├── MeetupParticipantRepository.cs │ │ │ └── MeetupRepository.cs │ │ ├── Job │ │ │ ├── IJobHistoryRepository.cs │ │ │ └── JobHistoryRepository.cs │ │ ├── BookUser │ │ │ ├── IBookUserRepository.cs │ │ │ └── BookUserRepository.cs │ │ ├── Category │ │ │ ├── ICategoryRepository.cs │ │ │ └── CategoryRepository.cs │ │ ├── User │ │ │ ├── IUserRepository.cs │ │ │ └── UserRepository.cs │ │ ├── AccessHistory │ │ │ ├── IAccessHistoryRepository.cs │ │ │ └── AccessHistoryRepository.cs │ │ └── IncludeList.cs │ ├── UoW │ │ ├── IUnitOfWork.cs │ │ └── UnitOfWork.cs │ ├── Mapping │ │ ├── MailBounceMap.cs │ │ ├── CategoryMap.cs │ │ ├── AccessHistoryMap.cs │ │ ├── JobHistoryMap.cs │ │ ├── LogEntryMap.cs │ │ ├── BookUserMap.cs │ │ ├── AddressMap.cs │ │ ├── BookMap.cs │ │ └── UserMap.cs │ ├── ShareBook.Infra.Data.csproj │ ├── ApplicationDbContextFactory.cs │ ├── UtcContext.cs │ └── ApplicationDbContext.cs ├── Sharebook.Jobs │ ├── Executor │ │ └── IJobExecutor.cs │ ├── Jobs │ │ ├── IJob.cs │ │ ├── 6 - MailSupressListUpdate.cs │ │ └── 4 - MeetupSearch.cs │ └── Sharebook.Jobs.csproj ├── ShareBook.Infra.CrossCutting.Identity │ ├── TokenConfigurations.cs │ ├── Interfaces │ │ └── IApplicationSignInManager.cs │ ├── ShareBook.Infra.CrossCutting.Identity.csproj │ ├── SigningConfigurations.cs │ └── ApplicationSignInManager.cs ├── ShareBook.Helper │ ├── Crypto │ │ ├── Salt.cs │ │ └── Hash.cs │ ├── ShareBook.Helper.csproj │ ├── Extensions │ │ ├── EnumeratorExtension.cs │ │ ├── AssemblyExtension.cs │ │ └── StringExtension.cs │ ├── Mapper │ │ └── Mapper.cs │ ├── DateTime │ │ └── DateTimeHelper.cs │ ├── ClientVersionValidation │ │ └── ClientVersionValidation.cs │ └── Image │ │ └── ImageHelper.cs └── ShareBook.Test.Unit │ ├── Mocks │ ├── BookUserMock.cs │ └── BookMock.cs │ ├── Services │ ├── RecaptchaServiceTests.cs │ └── ContactUsEmailServiceTests.cs │ ├── ShareBook.Test.Unit.csproj │ ├── Validators │ ├── ContactUsValidatorTests.cs │ └── BookValidatorTests.cs │ └── Jobs │ └── 4 - MeetupSearchTests.cs ├── .github └── workflows │ ├── dockerfile-validation.yml │ ├── dotnet-build-validation.yml │ └── postman-collection.yml ├── .claude └── settings.local.json ├── devops ├── .dockerignore └── Dockerfile └── CLAUDE.md /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "formulahendry.dotnet-test-explorer" 4 | ] 5 | } -------------------------------------------------------------------------------- /ShareBook/ShareBook.Test.Integration/GlobalUsings.cs: -------------------------------------------------------------------------------- 1 | global using FluentAssertions; 2 | global using ShareBook.Test.Integration.Setup; -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "dotnet-test-explorer.testProjectPath": "ShareBook/ShareBook.Test.Unit/ShareBook.Test.Unit.csproj" 3 | } -------------------------------------------------------------------------------- /ShareBook/ShareBook.Api/wwwroot/Images/Books/os-sete.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SharebookBR/sharebook-backend/HEAD/ShareBook/ShareBook.Api/wwwroot/Images/Books/os-sete.jpg -------------------------------------------------------------------------------- /ShareBook/ShareBook.Api/wwwroot/Images/Books/a-cabana.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SharebookBR/sharebook-backend/HEAD/ShareBook/ShareBook.Api/wwwroot/Images/Books/a-cabana.jpg -------------------------------------------------------------------------------- /ShareBook/ShareBook.Api/wwwroot/Images/Books/a-passagem.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SharebookBR/sharebook-backend/HEAD/ShareBook/ShareBook.Api/wwwroot/Images/Books/a-passagem.jpg -------------------------------------------------------------------------------- /ShareBook/ShareBook.Api/wwwroot/Images/Books/fantasia-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SharebookBR/sharebook-backend/HEAD/ShareBook/ShareBook.Api/wwwroot/Images/Books/fantasia-1.jpg -------------------------------------------------------------------------------- /ShareBook/ShareBook.Api/wwwroot/Images/Books/o-cortico.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SharebookBR/sharebook-backend/HEAD/ShareBook/ShareBook.Api/wwwroot/Images/Books/o-cortico.jpg -------------------------------------------------------------------------------- /ShareBook/ShareBook.Api/wwwroot/Images/Books/star-wars.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SharebookBR/sharebook-backend/HEAD/ShareBook/ShareBook.Api/wwwroot/Images/Books/star-wars.jpg -------------------------------------------------------------------------------- /ShareBook/ShareBook.Api/wwwroot/Images/Books/the-hobbit.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SharebookBR/sharebook-backend/HEAD/ShareBook/ShareBook.Api/wwwroot/Images/Books/the-hobbit.jpg -------------------------------------------------------------------------------- /ShareBook/ShareBook.Api/wwwroot/Images/Books/guerra-civil.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SharebookBR/sharebook-backend/HEAD/ShareBook/ShareBook.Api/wwwroot/Images/Books/guerra-civil.jpg -------------------------------------------------------------------------------- /ShareBook/ShareBook.Api/wwwroot/Images/Books/homem-aranha.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SharebookBR/sharebook-backend/HEAD/ShareBook/ShareBook.Api/wwwroot/Images/Books/homem-aranha.jpg -------------------------------------------------------------------------------- /ShareBook/ShareBook.Api/wwwroot/Images/Books/nunca-jamais.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SharebookBR/sharebook-backend/HEAD/ShareBook/ShareBook.Api/wwwroot/Images/Books/nunca-jamais.jpg -------------------------------------------------------------------------------- /ShareBook/ShareBook.Api/wwwroot/Images/Books/as-duas-torres.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SharebookBR/sharebook-backend/HEAD/ShareBook/ShareBook.Api/wwwroot/Images/Books/as-duas-torres.jpg -------------------------------------------------------------------------------- /ShareBook/ShareBook.Api/wwwroot/Images/Books/os-vingadores.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SharebookBR/sharebook-backend/HEAD/ShareBook/ShareBook.Api/wwwroot/Images/Books/os-vingadores.jpg -------------------------------------------------------------------------------- /ShareBook/ShareBook.Api/wwwroot/Images/Books/a-furia-dos-reis.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SharebookBR/sharebook-backend/HEAD/ShareBook/ShareBook.Api/wwwroot/Images/Books/a-furia-dos-reis.jpg -------------------------------------------------------------------------------- /ShareBook/ShareBook.Api/wwwroot/Images/Books/a-hora-do-vampiro.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SharebookBR/sharebook-backend/HEAD/ShareBook/ShareBook.Api/wwwroot/Images/Books/a-hora-do-vampiro.jpg -------------------------------------------------------------------------------- /ShareBook/ShareBook.Api/wwwroot/Images/Books/anjos-e-demonios.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SharebookBR/sharebook-backend/HEAD/ShareBook/ShareBook.Api/wwwroot/Images/Books/anjos-e-demonios.jpg -------------------------------------------------------------------------------- /ShareBook/ShareBook.Api/wwwroot/Images/Books/antologia-poetica.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SharebookBR/sharebook-backend/HEAD/ShareBook/ShareBook.Api/wwwroot/Images/Books/antologia-poetica.jpg -------------------------------------------------------------------------------- /ShareBook/ShareBook.Api/wwwroot/Images/Books/direito-e-justiça.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SharebookBR/sharebook-backend/HEAD/ShareBook/ShareBook.Api/wwwroot/Images/Books/direito-e-justiça.jpg -------------------------------------------------------------------------------- /ShareBook/ShareBook.Api/wwwroot/Images/Books/o-retorno-do-rei.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SharebookBR/sharebook-backend/HEAD/ShareBook/ShareBook.Api/wwwroot/Images/Books/o-retorno-do-rei.png -------------------------------------------------------------------------------- /ShareBook/ShareBook.Api/wwwroot/Images/Books/se-venden-gorras.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SharebookBR/sharebook-backend/HEAD/ShareBook/ShareBook.Api/wwwroot/Images/Books/se-venden-gorras.jpg -------------------------------------------------------------------------------- /ShareBook/ShareBook.Api/wwwroot/Images/Books/senhor-dos-aneis.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SharebookBR/sharebook-backend/HEAD/ShareBook/ShareBook.Api/wwwroot/Images/Books/senhor-dos-aneis.jpg -------------------------------------------------------------------------------- /ShareBook/ShareBook.Api/wwwroot/Images/Books/the-book-of-jonah.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SharebookBR/sharebook-backend/HEAD/ShareBook/ShareBook.Api/wwwroot/Images/Books/the-book-of-jonah.jpg -------------------------------------------------------------------------------- /ShareBook/ShareBook.Api/wwwroot/Images/Books/a-irmandade-do-anel.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SharebookBR/sharebook-backend/HEAD/ShareBook/ShareBook.Api/wwwroot/Images/Books/a-irmandade-do-anel.jpg -------------------------------------------------------------------------------- /ShareBook/ShareBook.Api/wwwroot/Images/Books/a-maldicao-do-cigano.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SharebookBR/sharebook-backend/HEAD/ShareBook/ShareBook.Api/wwwroot/Images/Books/a-maldicao-do-cigano.jpg -------------------------------------------------------------------------------- /ShareBook/ShareBook.Api/wwwroot/Images/Books/cronicas-gelo-e-fogo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SharebookBR/sharebook-backend/HEAD/ShareBook/ShareBook.Api/wwwroot/Images/Books/cronicas-gelo-e-fogo.jpg -------------------------------------------------------------------------------- /ShareBook/ShareBook.Api/wwwroot/Images/Books/direito-penal-rideel.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SharebookBR/sharebook-backend/HEAD/ShareBook/ShareBook.Api/wwwroot/Images/Books/direito-penal-rideel.jpg -------------------------------------------------------------------------------- /ShareBook/ShareBook.Api/wwwroot/Images/Books/num-vento-diferente.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SharebookBR/sharebook-backend/HEAD/ShareBook/ShareBook.Api/wwwroot/Images/Books/num-vento-diferente.jpg -------------------------------------------------------------------------------- /ShareBook/ShareBook.Api/wwwroot/Images/Books/o-festim-dos-corvos.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SharebookBR/sharebook-backend/HEAD/ShareBook/ShareBook.Api/wwwroot/Images/Books/o-festim-dos-corvos.jpg -------------------------------------------------------------------------------- /ShareBook/ShareBook.Api/wwwroot/Images/Books/o-pequeno-principe.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SharebookBR/sharebook-backend/HEAD/ShareBook/ShareBook.Api/wwwroot/Images/Books/o-pequeno-principe.jpg -------------------------------------------------------------------------------- /ShareBook/ShareBook.Domain/Enums/Profile.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace ShareBook.Domain.Enums 3 | { 4 | public enum Profile 5 | { 6 | Administrator, 7 | User 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Api/wwwroot/Images/Books/as-provacoes-de-apolo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SharebookBR/sharebook-backend/HEAD/ShareBook/ShareBook.Api/wwwroot/Images/Books/as-provacoes-de-apolo.jpg -------------------------------------------------------------------------------- /ShareBook/ShareBook.Api/wwwroot/Images/Books/o-segredo-das-sombras.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SharebookBR/sharebook-backend/HEAD/ShareBook/ShareBook.Api/wwwroot/Images/Books/o-segredo-das-sombras.jpg -------------------------------------------------------------------------------- /ShareBook/ShareBook.Api/wwwroot/Images/Books/orgulho-e-preconceito.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SharebookBR/sharebook-backend/HEAD/ShareBook/ShareBook.Api/wwwroot/Images/Books/orgulho-e-preconceito.jpg -------------------------------------------------------------------------------- /ShareBook/ShareBook.Api/wwwroot/Images/Books/programando-o-android.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SharebookBR/sharebook-backend/HEAD/ShareBook/ShareBook.Api/wwwroot/Images/Books/programando-o-android.jpg -------------------------------------------------------------------------------- /ShareBook/ShareBook.Api/wwwroot/Images/Books/introducao-a-programacao.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SharebookBR/sharebook-backend/HEAD/ShareBook/ShareBook.Api/wwwroot/Images/Books/introducao-a-programacao.jpg -------------------------------------------------------------------------------- /ShareBook/ShareBook.Api/wwwroot/Images/Books/novelas-nada-exemplares.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SharebookBR/sharebook-backend/HEAD/ShareBook/ShareBook.Api/wwwroot/Images/Books/novelas-nada-exemplares.jpg -------------------------------------------------------------------------------- /ShareBook/ShareBook.Api/wwwroot/Images/Books/volta-ao-mundo-em-80-dias.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SharebookBR/sharebook-backend/HEAD/ShareBook/ShareBook.Api/wwwroot/Images/Books/volta-ao-mundo-em-80-dias.jpg -------------------------------------------------------------------------------- /ShareBook/ShareBook.Api/wwwroot/Images/Books/a-menina-que-roubava-livros.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SharebookBR/sharebook-backend/HEAD/ShareBook/ShareBook.Api/wwwroot/Images/Books/a-menina-que-roubava-livros.jpg -------------------------------------------------------------------------------- /ShareBook/ShareBook.Api/wwwroot/Images/Books/c-sharp-programao-em-camada.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SharebookBR/sharebook-backend/HEAD/ShareBook/ShareBook.Api/wwwroot/Images/Books/c-sharp-programao-em-camada.jpg -------------------------------------------------------------------------------- /ShareBook/ShareBook.Api/wwwroot/Images/Books/programacao-de-jogo-android.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SharebookBR/sharebook-backend/HEAD/ShareBook/ShareBook.Api/wwwroot/Images/Books/programacao-de-jogo-android.jpg -------------------------------------------------------------------------------- /ShareBook/ShareBook.Api/ViewModels/ParentAprovalVM.cs: -------------------------------------------------------------------------------- 1 | namespace ShareBook.Api.ViewModels 2 | { 3 | public class ParentAprovalVM 4 | { 5 | public string ParentHashCodeAproval { get; set; } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Api/wwwroot/Images/Books/percy-jackson-e-os-olimpianos.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SharebookBR/sharebook-backend/HEAD/ShareBook/ShareBook.Api/wwwroot/Images/Books/percy-jackson-e-os-olimpianos.jpg -------------------------------------------------------------------------------- /ShareBook/ShareBook.Api/wwwroot/Images/Books/teoria-discursiva-do-direito.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SharebookBR/sharebook-backend/HEAD/ShareBook/ShareBook.Api/wwwroot/Images/Books/teoria-discursiva-do-direito.jpg -------------------------------------------------------------------------------- /ShareBook/ShareBook.Domain/Common/IIdProperty.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ShareBook.Domain.Common 4 | { 5 | public interface IIdProperty 6 | { 7 | Guid Id { get; set; } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Api/wwwroot/Images/Books/100-segredos-das-pessoas-felizes.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SharebookBR/sharebook-backend/HEAD/ShareBook/ShareBook.Api/wwwroot/Images/Books/100-segredos-das-pessoas-felizes.jpg -------------------------------------------------------------------------------- /ShareBook/ShareBook.Api/wwwroot/Images/Books/programacao-de-redes-com-python.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SharebookBR/sharebook-backend/HEAD/ShareBook/ShareBook.Api/wwwroot/Images/Books/programacao-de-redes-com-python.jpg -------------------------------------------------------------------------------- /ShareBook/ShareBook.Api/wwwroot/Images/Books/the-hobbit-there-and-back-again.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SharebookBR/sharebook-backend/HEAD/ShareBook/ShareBook.Api/wwwroot/Images/Books/the-hobbit-there-and-back-again.jpg -------------------------------------------------------------------------------- /ShareBook/ShareBook.Service/Meetup/MeetupSettings.cs: -------------------------------------------------------------------------------- 1 | namespace ShareBook.Service; 2 | 3 | public class MeetupSettings 4 | { 5 | public string YoutubeToken { get; set; } 6 | public bool IsActive { get; set; } 7 | } 8 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Api/wwwroot/Images/Books/harry-potter-prisioneiro-de-askaban.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SharebookBR/sharebook-backend/HEAD/ShareBook/ShareBook.Api/wwwroot/Images/Books/harry-potter-prisioneiro-de-askaban.jpg -------------------------------------------------------------------------------- /ShareBook/ShareBook.Repository/Repository/Book/IBookRepository.cs: -------------------------------------------------------------------------------- 1 | using ShareBook.Domain; 2 | 3 | namespace ShareBook.Repository 4 | { 5 | public interface IBookRepository : IRepositoryGeneric 6 | { 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Api/wwwroot/Images/Books/harry-potter-e-as-reliquias-da-morte.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SharebookBR/sharebook-backend/HEAD/ShareBook/ShareBook.Api/wwwroot/Images/Books/harry-potter-e-as-reliquias-da-morte.jpg -------------------------------------------------------------------------------- /ShareBook/ShareBook.Api/ViewModels/TrackinNumberBookVM.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | namespace ShareBook.Api.ViewModels 3 | { 4 | public class TrackinNumberBookVM 5 | { 6 | public string TrackingNumber { get; set; } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Domain/Category.cs: -------------------------------------------------------------------------------- 1 | using ShareBook.Domain.Common; 2 | 3 | namespace ShareBook.Domain 4 | { 5 | public class Category : BaseEntity 6 | { 7 | public string Name { get; set; } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Repository/Repository/Meetup/IMeetupRepository.cs: -------------------------------------------------------------------------------- 1 | using ShareBook.Domain; 2 | 3 | namespace ShareBook.Repository 4 | { 5 | public interface IMeetupRepository : IRepositoryGeneric 6 | { 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Repository/Repository/Job/IJobHistoryRepository.cs: -------------------------------------------------------------------------------- 1 | using ShareBook.Domain; 2 | 3 | namespace ShareBook.Repository 4 | { 5 | public interface IJobHistoryRepository : IRepositoryGeneric 6 | { 7 | } 8 | } -------------------------------------------------------------------------------- /ShareBook/ShareBook.Domain/Enums/TypeSegments.cs: -------------------------------------------------------------------------------- 1 | namespace ShareBook.Domain.Enums 2 | { 3 | public enum TypeSegments 4 | { 5 | Inactive = 0, 6 | Engaged = 1, 7 | Active = 2, 8 | All = 3 9 | } 10 | } -------------------------------------------------------------------------------- /ShareBook/ShareBook.Repository/Repository/BookUser/IBookUserRepository.cs: -------------------------------------------------------------------------------- 1 | using ShareBook.Domain; 2 | 3 | namespace ShareBook.Repository 4 | { 5 | public interface IBookUserRepository : IRepositoryGeneric 6 | { 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Repository/Repository/Category/ICategoryRepository.cs: -------------------------------------------------------------------------------- 1 | using ShareBook.Domain; 2 | 3 | namespace ShareBook.Repository 4 | { 5 | public interface ICategoryRepository : IRepositoryGeneric 6 | { 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /ShareBook/Sharebook.Jobs/Executor/IJobExecutor.cs: -------------------------------------------------------------------------------- 1 | using ShareBook.Domain.Common; 2 | using System.Threading.Tasks; 3 | 4 | namespace Sharebook.Jobs; 5 | 6 | public interface IJobExecutor 7 | { 8 | Task ExecuteAsync(); 9 | } 10 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Service/Category/ICategoryService.cs: -------------------------------------------------------------------------------- 1 | using ShareBook.Service.Generic; 2 | using ShareBook.Domain; 3 | 4 | namespace ShareBook.Service 5 | { 6 | public interface ICategoryService : IBaseService 7 | { 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Service/Server/ServerSettings.cs: -------------------------------------------------------------------------------- 1 | namespace ShareBook.Service.Server 2 | { 3 | public class ServerSettings 4 | { 5 | public string DefaultUrl { get; set; } 6 | public string JobExecutorToken { get; set; } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Api/.config/dotnet-tools.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "isRoot": true, 4 | "tools": { 5 | "dotnet-ef": { 6 | "version": "8.0.6", 7 | "commands": [ 8 | "dotnet-ef" 9 | ] 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /ShareBook/ShareBook.Domain/Enums/JobResult.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | namespace ShareBook.Domain.Enums 3 | { 4 | public enum JobResult 5 | { 6 | Success, 7 | Error, 8 | AwsSqsDisabled, 9 | MeetupDisabled, 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Repository/Repository/Meetup/IMeetupParticipantRepository.cs: -------------------------------------------------------------------------------- 1 | using ShareBook.Domain; 2 | 3 | namespace ShareBook.Repository 4 | { 5 | public interface IMeetupParticipantRepository : IRepositoryGeneric 6 | { 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Api/ViewModels/RequestBookVM.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | namespace ShareBook.Api.ViewModels 3 | { 4 | public class RequestBookVM 5 | { 6 | public Guid BookId { get; set; } 7 | 8 | public string Reason { get; set; } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Api/ViewModels/DonateBookUserVM.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ShareBook.Api.ViewModels 4 | { 5 | public class DonateBookUserVM 6 | { 7 | public Guid UserId { get; set; } 8 | public string Note { get; set; } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Domain/Enums/JobInterval.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | namespace ShareBook.Domain.Enums 3 | { 4 | public enum Interval 5 | { 6 | Weekly, 7 | Dayly, 8 | Hourly, 9 | Each30Minutes, 10 | Each5Minutes, 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Api/ViewModels/ChangePasswordUserVM.cs: -------------------------------------------------------------------------------- 1 | namespace ShareBook.Api.ViewModels 2 | { 3 | public class ChangePasswordUserVM 4 | { 5 | public string NewPassword { get; set; } 6 | 7 | public string OldPassword { get; set; } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Service/Email/IEmailTemplate.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace ShareBook.Service 4 | { 5 | public interface IEmailTemplate 6 | { 7 | Task GenerateHtmlFromTemplateAsync(string template, object model); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Service/Recaptcha/IRecaptchaService.cs: -------------------------------------------------------------------------------- 1 | using ShareBook.Domain.Common; 2 | 3 | namespace ShareBook.Service.Recaptcha 4 | { 5 | public interface IRecaptchaService 6 | { 7 | Result SimpleValidationRecaptcha(string recaptcha); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Api/ViewModels/ForgotMyPasswordVM.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | 3 | namespace ShareBook.Api.ViewModels 4 | { 5 | public class ForgotMyPasswordVM 6 | { 7 | [Required] 8 | public string Email { get; set; } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Service/Lgpd/ILgpdService.cs: -------------------------------------------------------------------------------- 1 | using ShareBook.Domain.DTOs; 2 | using System.Threading.Tasks; 3 | 4 | namespace ShareBook.Service.Lgpd 5 | { 6 | public interface ILgpdService 7 | { 8 | public Task AnonymizeAsync(UserAnonymizeDTO dto); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Api/ViewModels/AddFaciclitatorNotesVM.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ShareBook.Api.ViewModels 4 | { 5 | public class AddFacilitatorNotesVM 6 | { 7 | public Guid BookId { get; set; } 8 | 9 | public string FacilitatorNotes { get; set; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Domain/DTOs/BookStatsDTO.cs: -------------------------------------------------------------------------------- 1 | namespace ShareBook.Domain.DTOs 2 | { 3 | public class BookStatsDTO 4 | { 5 | public int TotalWaitingApproval { get; set; } 6 | public int TotalLate { get; set; } 7 | public int TotalOk { get; set; } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Service/ContactUs/IContactUsEmailService.cs: -------------------------------------------------------------------------------- 1 | using ShareBook.Domain; 2 | using System.Threading.Tasks; 3 | 4 | namespace ShareBook.Service 5 | { 6 | public interface IContactUsEmailService 7 | { 8 | Task SendEmailContactUsAsync(ContactUs contactUs); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Api/ViewModels/ChangeUserPasswordByEmailAndHashCodeVM.cs: -------------------------------------------------------------------------------- 1 | namespace ShareBook.Api.ViewModels 2 | { 3 | public class ChangeUserPasswordByHashCodeVM 4 | { 5 | public string HashCodePassword { get; set; } 6 | 7 | public string NewPassword { get; set; } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Repository/Repository/User/IUserRepository.cs: -------------------------------------------------------------------------------- 1 | using ShareBook.Domain; 2 | using System.Threading.Tasks; 3 | 4 | namespace ShareBook.Repository 5 | { 6 | public interface IUserRepository : IRepositoryGeneric 7 | { 8 | Task UpdatePasswordAsync(User user); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Api/ViewModels/AccessHistoryVM.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ShareBook.Api.ViewModels { 4 | public class AccessHistoryVM : BaseViewModel { 5 | public DateTime VisitingDay { get; set; } 6 | public string VisitorName { get; set; } 7 | public string Profile { get; set; } 8 | } 9 | } -------------------------------------------------------------------------------- /ShareBook/ShareBook.Service/Upload/ImageSettings.cs: -------------------------------------------------------------------------------- 1 | namespace ShareBook.Service.Upload 2 | { 3 | public class ImageSettings 4 | { 5 | public string BaseDirectory { get; set; } 6 | 7 | public string ImagePath { get; set; } 8 | 9 | public string EBookPdfPath { get; set; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Domain/Common/BaseEntity.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ShareBook.Domain.Common 4 | { 5 | public abstract class BaseEntity : IIdProperty 6 | { 7 | public Guid Id { get; set; } = Guid.NewGuid(); 8 | 9 | public DateTime? CreationDate { get; set; } = DateTime.Now; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Api/ViewModels/BaseViewModel.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using ShareBook.Domain.Common; 3 | using System; 4 | 5 | namespace ShareBook.Api.ViewModels 6 | { 7 | public abstract class BaseViewModel : IIdProperty 8 | { 9 | [JsonIgnore] 10 | public Guid Id { get; set; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Domain/DTOs/BookCancelationDTO.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ShareBook.Domain.DTOs 4 | { 5 | public class BookCancelationDTO 6 | { 7 | public Book Book { get; set; } 8 | public string CanceledBy { get; set; } 9 | public string Reason { get; set; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Domain/DTOs/UserAnonymizeDTO.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ShareBook.Domain.DTOs 4 | { 5 | public class UserAnonymizeDTO 6 | { 7 | public Guid UserId { get; set; } 8 | public string Password { get; set; } 9 | public string Reason { get; set; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Api/ViewModels/ApproveBookVM.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ShareBook.Api.ViewModels 4 | { 5 | public class ApproveBookVM 6 | { 7 | /// 8 | /// The date that the winner will be choosed 9 | /// 10 | public DateTime? ChooseDate { get; set; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Repository/UoW/IUnitOfWork.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | 4 | namespace ShareBook.Repository.UoW 5 | { 6 | public interface IUnitOfWork : IAsyncDisposable 7 | { 8 | Task BeginTransactionAsync(); 9 | Task CommitAsync(); 10 | Task RollbackAsync(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Domain/Common/JobExecutorResult.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace ShareBook.Domain.Common 6 | { 7 | public class JobExecutorResult 8 | { 9 | public bool Success { get; set; } 10 | public IList Messages { get; set; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Repository/Repository/Category/CategoryRepository.cs: -------------------------------------------------------------------------------- 1 | using ShareBook.Domain; 2 | 3 | namespace ShareBook.Repository 4 | { 5 | public class CategoryRepository : RepositoryGeneric, ICategoryRepository 6 | { 7 | public CategoryRepository(ApplicationDbContext context) : base(context) { } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Api/ViewModels/LoginUserVM.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | 3 | namespace ShareBook.Api.ViewModels 4 | { 5 | public class LoginUserVM 6 | { 7 | [Required] 8 | public string Email { get; set; } 9 | 10 | [Required] 11 | public string Password { get; set; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Infra.CrossCutting.Identity/TokenConfigurations.cs: -------------------------------------------------------------------------------- 1 | 2 | 3 | namespace ShareBook.Infra.CrossCutting.Identity 4 | 5 | { 6 | public class TokenConfigurations 7 | { 8 | public string Audience { get; set; } 9 | public string Issuer { get; set; } 10 | public int Seconds { get; set; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Service/AccessHistory/IAccessHistoryService.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using ShareBook.Domain; 3 | using ShareBook.Domain.Enums; 4 | 5 | namespace ShareBook.Service { 6 | public interface IAccessHistoryService 7 | { 8 | Task InsertVisitorAsync(User user, User visitor, VisitorProfile profile); 9 | } 10 | } -------------------------------------------------------------------------------- /ShareBook/ShareBook.Domain/Exceptions/AwsSqsDisabledException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.Serialization; 3 | 4 | namespace ShareBook.Domain.Exceptions; 5 | 6 | [Serializable] 7 | public class AwsSqsDisabledException : Exception 8 | { 9 | public AwsSqsDisabledException(string message) : base(message) 10 | { 11 | } 12 | } 13 | 14 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Domain/Exceptions/MeetupDisabledException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.Serialization; 3 | 4 | namespace ShareBook.Domain.Exceptions; 5 | 6 | [Serializable] 7 | public class MeetupDisabledException : Exception 8 | { 9 | public MeetupDisabledException(string message) : base(message) 10 | { 11 | } 12 | } 13 | 14 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Domain/Enums/VisitorProfile.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | 3 | namespace ShareBook.Domain.Enums { 4 | public enum VisitorProfile { 5 | [Description("Doador")] Donor, 6 | [Description("Ganhador")] Winner, 7 | [Description("Indefinido")] Undefined, 8 | [Description("Facilitador")] Facilitator 9 | } 10 | } -------------------------------------------------------------------------------- /ShareBook/ShareBook.Service/ContactUs/IContactUsService.cs: -------------------------------------------------------------------------------- 1 | using ShareBook.Domain; 2 | using ShareBook.Domain.Common; 3 | using System.Threading.Tasks; 4 | 5 | namespace ShareBook.Service 6 | { 7 | public interface IContactUsService 8 | { 9 | Task> SendContactUsAsync(ContactUs contactUs, string recaptchaReactive); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Api/ViewModels/CancelBookDonationVM.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ShareBook.Api.ViewModels 4 | { 5 | public class CancelBookDonationVM 6 | { 7 | public Guid Id { get; set; } 8 | public string Title { get; set; } 9 | public string Author { get; set; } 10 | public string Status { get; set; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Domain/ContactUs.cs: -------------------------------------------------------------------------------- 1 | using ShareBook.Domain.Common; 2 | 3 | namespace ShareBook.Domain 4 | { 5 | public class ContactUs : BaseEntity 6 | { 7 | public string Name { get; set; } 8 | public string Email { get; set; } 9 | public string Phone { get; set; } 10 | public string Message { get; set; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Repository/Repository/BookUser/BookUserRepository.cs: -------------------------------------------------------------------------------- 1 | using ShareBook.Domain; 2 | 3 | namespace ShareBook.Repository 4 | { 5 | public class BookUserRepository : RepositoryGeneric, IBookUserRepository 6 | { 7 | public BookUserRepository(ApplicationDbContext context) : base(context) 8 | { 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Api/ViewModels/EmailTestVM.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | 3 | namespace ShareBook.Api.ViewModels 4 | { 5 | public class EmailTestVM 6 | { 7 | [Required] 8 | [EmailAddress] 9 | public string Email { get; set; } 10 | 11 | [Required] 12 | public string Name { get; set; } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Api/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "ShareBook.Api": { 4 | "commandName": "Project", 5 | "launchBrowser": true, 6 | "launchUrl": "swagger", 7 | "applicationUrl": "http://localhost:8000/", 8 | "environmentVariables": { 9 | "ASPNETCORE_ENVIRONMENT": "Development" 10 | } 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /ShareBook/ShareBook.Domain/Common/PagedList.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace ShareBook.Domain.Common 4 | { 5 | public class PagedList 6 | { 7 | public int Page { get; set; } 8 | public int ItemsPerPage { get; set; } 9 | public int TotalItems { get; set; } 10 | public IList Items { get; set; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Service/AwsSqs/Dto/SharebookMessage.cs: -------------------------------------------------------------------------------- 1 | using AutoMapper.Configuration.Conventions; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | 6 | namespace ShareBook.Service.AwsSqs.Dto 7 | { 8 | public class SharebookMessage{ 9 | public string ReceiptHandle { get; set; } 10 | public T Body { get; set; } 11 | } 12 | } -------------------------------------------------------------------------------- /ShareBook/ShareBook.Api/ViewModels/ContactUsVM.cs: -------------------------------------------------------------------------------- 1 | namespace ShareBook.Api.ViewModels 2 | { 3 | public class ContactUsVM 4 | { 5 | public string Name { get; set; } 6 | public string Email { get; set; } 7 | public string Phone { get; set; } 8 | public string Message { get; set; } 9 | public string RecaptchaReactive { get; set; } 10 | } 11 | } -------------------------------------------------------------------------------- /.github/workflows/dockerfile-validation.yml: -------------------------------------------------------------------------------- 1 | name: Dockerfile validation 2 | 3 | on: 4 | pull_request: 5 | branches: [ develop, master ] 6 | 7 | jobs: 8 | 9 | build: 10 | 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - uses: actions/checkout@v2 15 | - name: Build the Docker image 16 | run: | 17 | docker build -t sharebook-api -f devops/Dockerfile . 18 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Domain/Enums/BookType.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | using System.Text.Json.Serialization; 3 | 4 | namespace ShareBook.Domain.Enums 5 | { 6 | [JsonConverter(typeof(JsonStringEnumConverter))] 7 | public enum BookType 8 | { 9 | [Description("Impresso")] 10 | Printed, 11 | [Description("Eletrônico")] 12 | Eletronic 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Service/AWSSQS/IAwsSqsQueue.cs: -------------------------------------------------------------------------------- 1 | using ShareBook.Service.AwsSqs.Dto; 2 | using System.Threading.Tasks; 3 | 4 | namespace ShareBook.Service.AwsSqs 5 | { 6 | public interface IAwsSqsQueue 7 | { 8 | Task SendMessageAsync(T message); 9 | 10 | Task> GetMessageAsync(); 11 | 12 | Task DeleteMessageAsync(string receiptHandle); 13 | } 14 | } -------------------------------------------------------------------------------- /ShareBook/ShareBook.Service/Book/IBooksEmailService.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using ShareBook.Domain; 3 | 4 | namespace ShareBook.Service 5 | { 6 | public interface IBooksEmailService 7 | { 8 | Task SendEmailNewBookInsertedAsync(Book book); 9 | 10 | Task SendEmailBookApprovedAsync(Book book); 11 | 12 | Task SendEmailBookReceivedAsync(Book book); 13 | } 14 | } -------------------------------------------------------------------------------- /ShareBook/ShareBook.Service/Muambator/MuambatorDTO.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace ShareBook.Service.Muambator 6 | { 7 | public class MuambatorDTO 8 | { 9 | public string Status { get; set; } 10 | 11 | public string Message { get; set; } 12 | 13 | public IList Results { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Repository/Repository/Job/JobHistoryRepository.cs: -------------------------------------------------------------------------------- 1 | using ShareBook.Domain; 2 | using System.Threading.Tasks; 3 | 4 | namespace ShareBook.Repository 5 | { 6 | public class JobHistoryRepository : RepositoryGeneric, IJobHistoryRepository 7 | { 8 | public JobHistoryRepository(ApplicationDbContext context) : base(context) 9 | { 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Service/PushNotification/PushNotificationSettings.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace ShareBook.Service.Notification 6 | { 7 | public class PushNotificationSettings 8 | { 9 | public bool IsActive { get; set; } 10 | public string AppId { get; set; } 11 | public string ApiKey { get; set; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Service/AwsSqs/Dto/NewBookBody.cs: -------------------------------------------------------------------------------- 1 | using AutoMapper.Configuration.Conventions; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | 6 | namespace ShareBook.Service.AwsSqs.Dto 7 | { 8 | public class NewBookBody{ 9 | 10 | public Guid BookId { get; set; } 11 | public string BookTitle { get; set; } 12 | public Guid CategoryId { get; set; } 13 | } 14 | } -------------------------------------------------------------------------------- /ShareBook/ShareBook.Service/Muambator/IMuambatorService.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using ShareBook.Domain; 3 | 4 | namespace ShareBook.Service.Muambator 5 | { 6 | public interface IMuambatorService 7 | { 8 | Task AddPackageToTrackerAsync(Book book, User winner, string packageNumber); 9 | 10 | Task RemovePackageToTrackerAsync(string packageNumber); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Repository/Repository/AccessHistory/IAccessHistoryRepository.cs: -------------------------------------------------------------------------------- 1 | using ShareBook.Domain; 2 | 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Threading.Tasks; 6 | 7 | namespace ShareBook.Repository { 8 | public interface IAccessHistoryRepository : IRepositoryGeneric { 9 | Task> GetWhoAccessedMyProfileAsync(Guid userId); 10 | } 11 | } -------------------------------------------------------------------------------- /ShareBook/ShareBook.Service/Meetup/IMeetupService.cs: -------------------------------------------------------------------------------- 1 | using ShareBook.Domain; 2 | using ShareBook.Service.Generic; 3 | using System.Collections.Generic; 4 | using System.Threading.Tasks; 5 | 6 | namespace ShareBook.Service 7 | { 8 | public interface IMeetupService : IBaseService 9 | { 10 | public Task> FetchMeetupsAsync(); 11 | Task> SearchAsync(string criteria); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Repository/Repository/IncludeList.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq.Expressions; 4 | 5 | namespace ShareBook.Repository.Repository 6 | { 7 | public class IncludeList : List>> 8 | { 9 | public IncludeList(params Expression>[] expressions) 10 | { 11 | AddRange(expressions); 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Infra.CrossCutting.Identity/Interfaces/IApplicationSignInManager.cs: -------------------------------------------------------------------------------- 1 | using ShareBook.Domain; 2 | using ShareBook.Infra.CrossCutting.Identity; 3 | 4 | namespace ShareBook.Infra.CrossCutting.Identity.Interfaces 5 | { 6 | public interface IApplicationSignInManager 7 | { 8 | object GenerateTokenAndSetIdentity(User user, SigningConfigurations signingConfigurations, TokenConfigurations tokenConfigurations); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Service/Upload/IUploadService.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace ShareBook.Service.Upload 4 | { 5 | public interface IUploadService 6 | { 7 | Task UploadImageAsync(byte[] imageBytes, string imageName, string lastDirectory); 8 | Task UploadPdfAsync(byte[] imageBytes, string imageName, string lastDirectory); 9 | string GetImageUrl(string imageName, string lastDirectory); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /.claude/settings.local.json: -------------------------------------------------------------------------------- 1 | { 2 | "permissions": { 3 | "allow": [ 4 | "WebFetch(domain:github.com)", 5 | "Read(//c/REPOS/_PEGASUS/pegasus-core-api/**)", 6 | "Bash(dotnet build)", 7 | "Bash(dotnet run:*)", 8 | "Bash(dotnet build:*)", 9 | "Bash(dir:*)", 10 | "Bash(findstr:*)", 11 | "Read(//c/Users/brnra019/Documents/Lightshot/**)", 12 | "Bash(git rm:*)" 13 | ], 14 | "deny": [], 15 | "ask": [] 16 | } 17 | } -------------------------------------------------------------------------------- /ShareBook/ShareBook.Service/Email/EmailSettings.cs: -------------------------------------------------------------------------------- 1 | namespace ShareBook.Service 2 | { 3 | public class EmailSettings 4 | { 5 | public string HostName { get; set; } 6 | public string Username { get; set; } 7 | public string Password { get; set; } 8 | public int Port { get; set; } 9 | public bool UseSSL { get; set; } 10 | public int ImapPort { get; set; } 11 | public string BounceFolder { get; set; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Api/Filters/GetClaimsFilter.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc.Filters; 2 | using System.Threading; 3 | 4 | namespace ShareBook.Api.Filters 5 | { 6 | public class GetClaimsFilterAttribute : ActionFilterAttribute 7 | { 8 | public override void OnActionExecuting(ActionExecutingContext context) 9 | { 10 | Thread.CurrentPrincipal = context.HttpContext.User; 11 | base.OnActionExecuting(context); 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Infra.CrossCutting.Identity/ShareBook.Infra.CrossCutting.Identity.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Repository/Mapping/MailBounceMap.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using Microsoft.EntityFrameworkCore.Metadata.Builders; 3 | using ShareBook.Domain; 4 | 5 | namespace ShareBook.Repository.Mapping 6 | { 7 | public class MailBounceMap : IEntityTypeConfiguration 8 | { 9 | public void Configure(EntityTypeBuilder entityBuilder) 10 | { 11 | entityBuilder.HasIndex("Email"); 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Domain/DTOs/UserStatsDTO.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ShareBook.Domain.DTOs 4 | { 5 | public class UserStatsDTO 6 | { 7 | public DateTime? CreationDate { get; set; } 8 | public int TotalLate { get; set; } 9 | public int TotalOk { get; set; } 10 | public int TotalCanceled { get; set; } 11 | public int TotalWaitingApproval { get; set; } 12 | public int TotalAvailable { get; set; } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Domain/LogEntry.cs: -------------------------------------------------------------------------------- 1 | using ShareBook.Domain.Common; 2 | using System; 3 | 4 | namespace ShareBook.Domain 5 | { 6 | public class LogEntry : BaseEntity 7 | { 8 | public Guid? UserId { get; set; } 9 | public string EntityName { get; set; } 10 | public Guid EntityId { get; set; } 11 | public string Operation { get; set; } 12 | public DateTime LogDateTime { get; set; } 13 | public string ValuesChanges { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Service/AwsSqs/MailSenderLowPriorityQueue.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Options; 2 | using ShareBook.Service.AwsSqs.Dto; 3 | 4 | namespace ShareBook.Service.AwsSqs; 5 | 6 | public class MailSenderLowPriorityQueue : GenericQueue 7 | { 8 | public MailSenderLowPriorityQueue(IOptions awsSqsSettings) : base(awsSqsSettings) 9 | { 10 | _queueUrl = $"{_awsSqsSettings.QueueBaseUrl}/{_awsSqsSettings.SendEmailLowPriorityQueue}"; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Api/Program.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore; 2 | using Microsoft.AspNetCore.Hosting; 3 | 4 | namespace ShareBook.Api 5 | { 6 | public class Program 7 | { 8 | public static void Main(string[] args) 9 | { 10 | BuildWebHost(args).Run(); 11 | } 12 | 13 | public static IWebHost BuildWebHost(string[] args) => 14 | WebHost.CreateDefaultBuilder(args) 15 | .UseStartup() 16 | .Build(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /ShareBook/Sharebook.Jobs/Jobs/IJob.cs: -------------------------------------------------------------------------------- 1 | using ShareBook.Domain.Enums; 2 | using System; 3 | using System.Threading.Tasks; 4 | 5 | namespace Sharebook.Jobs 6 | { 7 | public interface IJob 8 | { 9 | string JobName { get; set; } 10 | string Description { get; set; } 11 | Interval Interval { get; set; } 12 | bool Active { get; set; } 13 | TimeSpan? BestTimeToExecute { get; set; } 14 | 15 | bool HasWork(); 16 | Task ExecuteAsync(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Domain/MeetupParticipant.cs: -------------------------------------------------------------------------------- 1 | using ShareBook.Domain.Common; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace ShareBook.Domain 9 | { 10 | public class MeetupParticipant : BaseEntity 11 | { 12 | public virtual Meetup Meetup { get; set; } 13 | public string FirstName { get; set; } 14 | public string LastName { get; set; } 15 | public string Email { get; set; } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Service/Email/Templates/ForgotPasswordTemplate.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Recuperar senha - Sharebook 6 | 7 | 8 |

9 | Olá {User.Name} 10 |

11 |

12 | Para realizar a troca da senha é necessário acessar o link {LinkForgotMyPassword} 13 |

14 |

Esse link tem validade de 24 horas

15 |

Equipe Sharebook

16 | 17 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Helper/Crypto/Salt.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Security.Cryptography; 3 | 4 | namespace ShareBook.Helper.Crypto 5 | { 6 | public class Salt 7 | { 8 | public static string Create() 9 | { 10 | byte[] randomBytes = new byte[128 / 8]; 11 | using (var generator = RandomNumberGenerator.Create()) 12 | { 13 | generator.GetBytes(randomBytes); 14 | return Convert.ToBase64String(randomBytes); 15 | } 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Service/PushNotification/IPushNotificationService.cs: -------------------------------------------------------------------------------- 1 | using ShareBook.Domain; 2 | using System.Threading.Tasks; 3 | 4 | namespace ShareBook.Service.Notification 5 | { 6 | public interface IPushNotificationService 7 | { 8 | Task SendNotificationSegmentsAsync(NotificationOnesignal notficationSettings); 9 | Task SendNotificationByKeyAsync(NotificationOnesignal notficationSettings); 10 | Task SendNotificationByEmailAsync(string email, string title, string content); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Domain/Validators/CategoryValidator.cs: -------------------------------------------------------------------------------- 1 | using FluentValidation; 2 | 3 | namespace ShareBook.Domain.Validators 4 | { 5 | public class CategoryValidator : AbstractValidator 6 | { 7 | 8 | #region Messages 9 | public const string Name = "O nome da categoria é obrigatório"; 10 | #endregion 11 | 12 | 13 | public CategoryValidator() 14 | { 15 | RuleFor(c => c.Name) 16 | .NotEmpty() 17 | .WithMessage(Name); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Service/User/IUserEmailService.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using ShareBook.Domain; 3 | using ShareBook.Domain.DTOs; 4 | 5 | namespace ShareBook.Service 6 | { 7 | public interface IUserEmailService 8 | { 9 | Task SendEmailForgotMyPasswordToUserAsync(User user); 10 | Task SendEmailRequestParentAprovalAsync(RegisterUserDTO userDto, User user); 11 | Task SendEmailParentAprovedNotifyUserAsync(User user); 12 | Task SendEmailAnonymizeNotifyAdmsAsync(UserAnonymizeDTO dto); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Domain/Enums/DonationStatus.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | using System.Text.Json.Serialization; 3 | 4 | namespace ShareBook.Domain.Enums 5 | { 6 | [JsonConverter(typeof(JsonStringEnumConverter))] 7 | public enum DonationStatus 8 | { 9 | [Description("Aguardando Ação")] 10 | WaitingAction, 11 | 12 | [Description("Doado")] 13 | Donated, 14 | 15 | [Description("Não foi dessa vez")] 16 | Denied, 17 | 18 | [Description("Cancelado")] 19 | Canceled 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Service/Authorization/Permission.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace ShareBook.Service.Authorization 4 | { 5 | public class Permissions 6 | { 7 | public enum Permission 8 | { 9 | CreateBook, 10 | UpdateBook, 11 | DeleteBook, 12 | ApproveBook, 13 | DonateBook 14 | } 15 | 16 | public static List AdminPermissions { get; } = new List() { Permission.ApproveBook, Permission.DonateBook }; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Service/AWSSQS/NewBookQueue.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Options; 2 | using ShareBook.Service.AwsSqs.Dto; 3 | 4 | namespace ShareBook.Service.AwsSqs; 5 | 6 | public class NewBookQueue : GenericQueue 7 | { 8 | 9 | public NewBookQueue(IOptions awsSqsSettings) : base(awsSqsSettings) 10 | { 11 | _queueUrl = $"{_awsSqsSettings.QueueBaseUrl}/{_awsSqsSettings.NewBookQueue}"; 12 | } 13 | 14 | // pra poder mockar 15 | public NewBookQueue() : base(null) {} 16 | } 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Test.Unit/Mocks/BookUserMock.cs: -------------------------------------------------------------------------------- 1 | using ShareBook.Domain; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | 6 | namespace ShareBook.Test.Unit.Mocks 7 | { 8 | public class BookUserMock 9 | { 10 | public static BookUser GetDonation(Book book, User requestingUser) 11 | { 12 | return new BookUser() 13 | { 14 | Book = book, 15 | User = requestingUser, 16 | Reason = "MOTIVO" 17 | }; 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Helper/ShareBook.Helper.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Domain/Enums/FreightOption.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | using System.Text.Json.Serialization; 3 | 4 | namespace ShareBook.Domain.Enums 5 | { 6 | [JsonConverter(typeof(JsonStringEnumConverter))] 7 | public enum FreightOption 8 | { 9 | [Description("Cidade")] 10 | City, 11 | 12 | [Description("Estado")] 13 | State, 14 | 15 | [Description("País")] 16 | Country, 17 | 18 | [Description("Mundo")] 19 | World, 20 | 21 | [Description("Não")] 22 | WithoutFreight, 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Domain/ShareBook.Domain.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Service/AwsSqs/MailSenderHighPriorityQueue .cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Microsoft.Extensions.Configuration; 3 | using Microsoft.Extensions.Options; 4 | using ShareBook.Service.AwsSqs.Dto; 5 | 6 | namespace ShareBook.Service.AwsSqs; 7 | 8 | public class MailSenderHighPriorityQueue : GenericQueue 9 | { 10 | public MailSenderHighPriorityQueue(IOptions awsSqsSettings) : base(awsSqsSettings) 11 | { 12 | _queueUrl = $"{_awsSqsSettings.QueueBaseUrl}/{_awsSqsSettings.SendEmailHighPriorityQueue}"; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Service/Category/CategoryService.cs: -------------------------------------------------------------------------------- 1 | using FluentValidation; 2 | using ShareBook.Domain; 3 | using ShareBook.Repository; 4 | using ShareBook.Repository.UoW; 5 | using ShareBook.Service.Generic; 6 | 7 | namespace ShareBook.Service 8 | { 9 | public class CategoryService : BaseService, ICategoryService 10 | { 11 | public CategoryService(ICategoryRepository categoryRepository, IUnitOfWork unitOfWork, IValidator validator) 12 | : base(categoryRepository, unitOfWork, validator) 13 | { 14 | 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Domain/NotificationOnesignal.cs: -------------------------------------------------------------------------------- 1 | using ShareBook.Domain.Enums; 2 | using System; 3 | using System.Collections.Generic; 4 | 5 | namespace ShareBook.Domain 6 | { 7 | public class NotificationOnesignal 8 | { 9 | public string Title { get; set; } 10 | public string Content { get; set; } 11 | public TypeSegments TypeSegments { get; set; } 12 | public string UrlImage { get; set; } 13 | public IList LanguageCodes { get; set; } 14 | public string Key { get; set; } 15 | public string Value { get; set; } 16 | } 17 | 18 | 19 | 20 | } 21 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Repository/Mapping/CategoryMap.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using Microsoft.EntityFrameworkCore.Metadata.Builders; 3 | using ShareBook.Domain; 4 | 5 | namespace ShareBook.Repository.Mapping 6 | { 7 | public class CategoryMap : IEntityTypeConfiguration 8 | { 9 | public void Configure(EntityTypeBuilder entityBuilder) 10 | { 11 | 12 | entityBuilder.HasKey(t => t.Id); 13 | 14 | entityBuilder.Property(t => t.Name) 15 | .HasMaxLength(100) 16 | .IsRequired(); 17 | 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Service/Meetup/Dto/YoutubeDTOError.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace ShareBook.Service.Dto; 4 | 5 | public class YoutubeDtoError 6 | { 7 | public Error error { get; set; } 8 | } 9 | 10 | public class Error 11 | { 12 | public int Code { get; set; } 13 | public string Message { get; set; } 14 | public List Errors { get; set; } 15 | public string Status { get; set; } 16 | } 17 | 18 | public class YoutubeErrorDetailDto 19 | { 20 | public string Message { get; set; } 21 | public string Domain { get; set; } 22 | public string Reason { get; set; } 23 | } 24 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Repository/Repository/Meetup/MeetupParticipantRepository.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using ShareBook.Domain; 3 | using ShareBook.Domain.Common; 4 | using ShareBook.Repository.Repository; 5 | using System; 6 | using System.Linq; 7 | using System.Linq.Expressions; 8 | using System.Threading.Tasks; 9 | 10 | namespace ShareBook.Repository 11 | { 12 | public class MeetupParticipantRepository : RepositoryGeneric, IMeetupParticipantRepository 13 | { 14 | public MeetupParticipantRepository(ApplicationDbContext context) : base(context) 15 | { 16 | 17 | } 18 | 19 | 20 | } 21 | } -------------------------------------------------------------------------------- /ShareBook/ShareBook.Service/AwsSqs/Dto/MailSenderBody.cs: -------------------------------------------------------------------------------- 1 | using AutoMapper.Configuration.Conventions; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | 6 | namespace ShareBook.Service.AwsSqs.Dto 7 | { 8 | public class MailSenderbody 9 | { 10 | public string Subject { get; set; } 11 | public string BodyHTML { get; set; } 12 | public IList Destinations { get; set; } 13 | public bool CopyAdmins { get; set; } = false; 14 | } 15 | 16 | public class Destination 17 | { 18 | public string Name { get; set; } 19 | public string Email { get; set; } 20 | } 21 | } -------------------------------------------------------------------------------- /ShareBook/ShareBook.Domain/Address.cs: -------------------------------------------------------------------------------- 1 | using ShareBook.Domain.Common; 2 | using System; 3 | 4 | namespace ShareBook.Domain 5 | { 6 | public class Address : BaseEntity 7 | { 8 | public string Street { get; set; } 9 | 10 | public string Number { get; set; } 11 | 12 | public string Complement { get; set; } 13 | 14 | public string Neighborhood { get; set; } 15 | 16 | public string PostalCode { get; set; } 17 | 18 | public string City { get; set; } 19 | 20 | public string State { get; set; } 21 | 22 | public string Country { get; set; } 23 | 24 | public Guid UserId { get; set; } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Api/ViewModels/NotificationOnesignalVM.cs: -------------------------------------------------------------------------------- 1 | using ShareBook.Domain.Enums; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.DataAnnotations; 4 | 5 | namespace ShareBook.Api.ViewModels 6 | { 7 | public class NotificationOnesignalVM 8 | { 9 | [Required] 10 | public string Title { get; set; } 11 | [Required] 12 | public string Content { get; set; } 13 | public TypeSegments TypeSegments { get; set; } 14 | public string UrlImage { get; set; } 15 | [Required] 16 | public string Key { get; set; } 17 | [Required] 18 | public string Value { get; set; } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Domain/Validators/AccessHistoryValidator.cs: -------------------------------------------------------------------------------- 1 | using FluentValidation; 2 | 3 | namespace ShareBook.Domain.Validators { 4 | public class AccessHistoryValidator : AbstractValidator { 5 | private const string nameRequired = "O nome é obrigatório"; 6 | private const string nameMaxLength = "Nome deve ter no máximo 100 caracteres"; 7 | 8 | public AccessHistoryValidator() { 9 | RuleFor(a => a.VisitorName) 10 | .NotEmpty() 11 | .WithMessage(nameRequired) 12 | .Must(x => x != null && x.Length < 200) 13 | .WithMessage(nameMaxLength); 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /ShareBook/ShareBook.Service/Recaptcha/RecaptchaService.cs: -------------------------------------------------------------------------------- 1 | using ShareBook.Domain.Common; 2 | 3 | namespace ShareBook.Service.Recaptcha 4 | { 5 | public class RecaptchaService : IRecaptchaService 6 | { 7 | // TODO: Fazer a validação real do "RecaptchaReactive" (https://developers.google.com/recaptcha/docs/verify) 8 | public Result SimpleValidationRecaptcha(string recaptcha) 9 | { 10 | Result result = new Result(); 11 | if (string.IsNullOrWhiteSpace(recaptcha) || recaptcha.Length <= 100) 12 | result.Messages.Add("RecaptchaReactive está inválido!"); 13 | 14 | return result; 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Api/ViewModels/UpdateUserVM.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using ShareBook.Domain; 3 | using System.ComponentModel.DataAnnotations; 4 | 5 | namespace ShareBook.Api.ViewModels 6 | { 7 | public class UpdateUserVM : BaseViewModel 8 | { 9 | [Required] 10 | public string Name { get; set; } 11 | 12 | [Required] 13 | public string Email { get; set; } 14 | 15 | public string Linkedin { get; set; } 16 | 17 | public string Instagram { get; set; } 18 | 19 | public Address Address { get; set; } 20 | 21 | public string Phone { get; set; } 22 | 23 | public bool AllowSendingEmail { get; set; } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Service/Email/Templates/BookCanceledTemplate.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Uma doação foi cancelada. 6 | 7 | 8 |

9 | Olá, 10 |

11 |

12 | Uma doação foi cancelada. Veja mais informações abaixo: 13 |

14 | 15 |
    16 |
  • Livro: {Title}
  • 17 |
  • Doador(a): {Donor}
  • 18 |
  • Cancelado por: {CanceledBy}
  • 19 |
  • Justificativa: {Reason}
  • 20 |
21 | 22 |

Sharebook

23 | 24 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Service/AWSSQS/AWSSQSSettings.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace ShareBook.Service.AwsSqs 6 | { 7 | public class AwsSqsSettings 8 | { 9 | public bool IsActive { get; set; } 10 | public string AccessKey { get; set; } 11 | public string SecretKey { get; set; } 12 | public string Region { get; set; } 13 | public string QueueBaseUrl { get; set; } 14 | 15 | // Queues 16 | public string NewBookQueue { get; set; } 17 | 18 | public string SendEmailHighPriorityQueue { get; set; } 19 | 20 | public string SendEmailLowPriorityQueue { get; set; } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Api/Controllers/CategoryController.cs: -------------------------------------------------------------------------------- 1 | using AutoMapper; 2 | using Microsoft.AspNetCore.Mvc; 3 | using ShareBook.Domain; 4 | using ShareBook.Domain.Common; 5 | using ShareBook.Service; 6 | using System.Threading.Tasks; 7 | 8 | namespace ShareBook.Api.Controllers 9 | { 10 | [Route("api/[controller]")] 11 | public class CategoryController : BaseCrudController 12 | { 13 | public CategoryController(ICategoryService categoryService, 14 | IMapper mapper) : base(categoryService, mapper) 15 | { 16 | SetDefault(x => x.Name); 17 | } 18 | 19 | public override async Task> GetAllAsync() => await PagedAsync(1, 50); 20 | } 21 | } -------------------------------------------------------------------------------- /ShareBook/ShareBook.Api/ViewModels/RequestersListVM.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using ShareBook.Domain; 3 | using ShareBook.Domain.Enums; 4 | using System; 5 | using System.ComponentModel.DataAnnotations; 6 | 7 | namespace ShareBook.Api.ViewModels 8 | { 9 | public class RequestersListVM 10 | { 11 | public Guid UserId { get; set; } 12 | 13 | public string RequesterNickName { get; set; } 14 | 15 | public string Location { get; set; } 16 | 17 | public int TotalBooksWon { get; set; } 18 | 19 | public int TotalBooksDonated { get; set; } 20 | 21 | public string RequestText { get; set; } 22 | 23 | public DonationStatus Status { get; set; } 24 | 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Domain/Meetup.cs: -------------------------------------------------------------------------------- 1 | using ShareBook.Domain.Common; 2 | using System; 3 | using System.Collections.Generic; 4 | 5 | namespace ShareBook.Domain; 6 | 7 | public class Meetup : BaseEntity 8 | { 9 | public int SymplaEventId { get; set; } 10 | public string Title { get; set; } 11 | public string Description { get; set; } 12 | public DateTime StartDate { get; set; } 13 | public string Cover { get; set; } 14 | public string YoutubeUrl { get; set; } 15 | public string SymplaEventUrl { get; set; } 16 | public ICollection MeetupParticipants { get; set; } 17 | public bool IsParticipantListSynced { get; set; } = false; 18 | public bool Active { get; set; } = true; 19 | } -------------------------------------------------------------------------------- /ShareBook/ShareBook.Service/Muambator/MuambatorConfigurator.cs: -------------------------------------------------------------------------------- 1 | 2 | using System; 3 | 4 | namespace ShareBook.Service.Muambator 5 | { 6 | public static class MuambatorConfigurator 7 | { 8 | public static string Token { get; private set; } 9 | 10 | public static bool IsActive { get; private set; } 11 | 12 | public static void Configure(string token, string isActive) 13 | { 14 | if ((string.IsNullOrEmpty(token) || string.IsNullOrEmpty(isActive)) || (isActive.ToLower() != "true" && isActive.ToLower() != "false")) 15 | return; 16 | 17 | Token = token; 18 | IsActive = Convert.ToBoolean(isActive.ToLower()); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Api/ViewModels/CustomValidators/StringLengthRangeOptionalAttribute.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | 3 | namespace ShareBook.Api.ViewModels.CustomValidators 4 | { 5 | public class StringLengthRangeOptionalAttribute : ValidationAttribute 6 | { 7 | public int Minimum { get; set; } 8 | public int Maximum { get; set; } 9 | 10 | public override bool IsValid(object value) 11 | { 12 | string model = (string)value; 13 | 14 | if (value == null || model.Length == 0 || model == string.Empty) 15 | return true; 16 | 17 | return model.Length > Minimum && model.Length <= Maximum; 18 | } 19 | 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Repository/Mapping/AccessHistoryMap.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using Microsoft.EntityFrameworkCore.Metadata.Builders; 3 | using ShareBook.Domain; 4 | 5 | namespace ShareBook.Repository.Mapping { 6 | public class AccessHistoryMap : IEntityTypeConfiguration { 7 | public void Configure(EntityTypeBuilder builder) { 8 | builder.ToTable("AccessHistories"); 9 | 10 | builder.HasKey(a => a.Id); 11 | builder.Property(a => a.VisitorName).HasMaxLength(200); 12 | builder.HasOne(a => a.User) 13 | .WithMany(u => u.Visitors) 14 | .HasForeignKey(a => a.UserId); 15 | 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /ShareBook/ShareBook.Infra.CrossCutting.Identity/SigningConfigurations.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.IdentityModel.Tokens; 2 | using System.Security.Cryptography; 3 | using System.Text; 4 | 5 | namespace ShareBook.Infra.CrossCutting.Identity 6 | { 7 | public class SigningConfigurations 8 | { 9 | public SecurityKey Key { get; } 10 | public SigningCredentials SigningCredentials { get; } 11 | 12 | public SigningConfigurations(string secretJwtKey) 13 | { 14 | Key = new SymmetricSecurityKey( 15 | Encoding.UTF8.GetBytes(secretJwtKey)); 16 | 17 | SigningCredentials = new SigningCredentials( 18 | Key, SecurityAlgorithms.HmacSha256Signature); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /ShareBook/Sharebook.Jobs/Sharebook.Jobs.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Api/ViewModels/CreateBookVM.cs: -------------------------------------------------------------------------------- 1 | using ShareBook.Domain.Enums; 2 | using System; 3 | 4 | namespace ShareBook.Api.ViewModels 5 | { 6 | public class CreateBookVM : BaseViewModel 7 | { 8 | public string Title { get; set; } 9 | 10 | public string Author { get; set; } 11 | 12 | public Guid CategoryId { get; set; } 13 | 14 | public string ImageName { get; set; } 15 | 16 | public byte[] ImageBytes { get; set; } 17 | 18 | public FreightOption FreightOption { get; set; } 19 | 20 | public string Synopsis { get; set; } 21 | public string Type { get; set; } 22 | public string EBookDownloadLink { get; set; } 23 | public string EBookPdfFile { get; set; } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Helper/Crypto/Hash.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | using Microsoft.AspNetCore.Cryptography.KeyDerivation; 4 | 5 | namespace ShareBook.Helper.Crypto 6 | { 7 | public class Hash 8 | { 9 | public static string Create(string value, string salt) 10 | { 11 | var valueBytes = KeyDerivation.Pbkdf2( 12 | password: value, 13 | salt: Encoding.UTF8.GetBytes(salt), 14 | prf: KeyDerivationPrf.HMACSHA512, 15 | iterationCount: 10000, 16 | numBytesRequested: 256 / 8); 17 | 18 | return Convert.ToBase64String(valueBytes); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Repository/Repository/User/UserRepository.cs: -------------------------------------------------------------------------------- 1 | using ShareBook.Domain; 2 | using System.Threading.Tasks; 3 | 4 | namespace ShareBook.Repository 5 | { 6 | public class UserRepository : RepositoryGeneric, IUserRepository 7 | { 8 | public UserRepository(ApplicationDbContext context) : base(context) 9 | { 10 | } 11 | 12 | public async Task UpdatePasswordAsync(User user) 13 | { 14 | _dbSet.Update(user); 15 | _context.Entry(user).Property(x => x.Password).IsModified = true; 16 | _context.Entry(user).Property(x => x.PasswordSalt).IsModified = true; 17 | await _context.SaveChangesAsync(); 18 | 19 | return user; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /.github/workflows/dotnet-build-validation.yml: -------------------------------------------------------------------------------- 1 | name: .NET build validation 2 | 3 | on: 4 | push: 5 | branches: [develop] 6 | pull_request: 7 | branches: [develop] 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - uses: actions/checkout@v2 15 | - name: Setup .NET 16 | uses: actions/setup-dotnet@v1 17 | with: 18 | dotnet-version: 8.0.x 19 | - name: Restore dependencies 20 | run: dotnet restore ./ShareBook/ShareBook.sln 21 | - name: Build 22 | run: dotnet build ./ShareBook/ShareBook.sln --no-restore 23 | - name: Test 24 | run: dotnet test ./ShareBook/ShareBook.Test.Unit/ShareBook.Test.Unit.csproj --no-build --verbosity normal 25 | if: success() -------------------------------------------------------------------------------- /ShareBook/ShareBook.Service/Meetup/Dto/YoutubeDetailDTO.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace ShareBook.Service.Dto; 5 | 6 | public class YoutubeDtoDetail 7 | { 8 | public string nextPageToken { get; set; } 9 | public string prevPageToken { get; set; } 10 | public PageInfo pageInfo { get; set; } 11 | public List Items { get; set; } 12 | 13 | } 14 | 15 | public class ItemDetail 16 | { 17 | public String Id { get; set; } 18 | public Snippet Snippet { get; set; } 19 | public liveStreamingDetails liveStreamingDetails { get; set; } 20 | } 21 | 22 | public class liveStreamingDetails 23 | { 24 | public DateTime scheduledStartTime { get; set; } 25 | public string activeLiveChatId { get; set; } 26 | } 27 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Service/Email/Templates/AnonymizeNotifyAdms.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Anonimização de conta 7 | 8 | 9 |

10 | Prezados adms. Um usuário solicitou a remoção de sua conta. Seus dados pessoais foram anonimizados e sua conta foi desativada. 11 |

12 |

13 | Justificativa
14 | {Reason} 15 |

16 | 17 |

18 | Atenciosamente,

19 | 20 | Sharebook team - Compartilhando conhecimento
21 | Siga-nos no instagram: 22 | @sharebook.com.br 23 |

24 | 25 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Api/ViewModels/MyBooksRequestsVM.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ShareBook.Api.ViewModels 4 | { 5 | public class MyBookRequestVM 6 | { 7 | public Guid RequestId { get; set; } 8 | 9 | public string Title { get; set; } 10 | 11 | public string Author { get; set; } 12 | 13 | // status do pedido. (ex: aguardando decisão, negado, doado) 14 | // apenas pra saber se ganhou ou não. 15 | public string Status { get; set; } 16 | 17 | // status da doação. (ex: aguardando envio, enviado, recebido) 18 | // para saber sobre o envio. 19 | public string BookStatus { get; set; } 20 | 21 | public string TrackingNumber { get; set; } 22 | 23 | public Guid BookId { get; set; } 24 | 25 | public string Slug { get; set; } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /.github/workflows/postman-collection.yml: -------------------------------------------------------------------------------- 1 | name: Postman collection validations 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | jobs: 7 | run-postman-collection-dev: 8 | if: false # desabilita temporariamente 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@master 12 | - name: Postman collection - Development environment 13 | uses: matt-ball/newman-action@master 14 | with: 15 | collection: ShareBook API - Tests.postman_collection.json 16 | delayRequest: 2000 17 | envVar: '[{ "key": "sharebookUrl", "value": "https://dev.sharebook.com.br/" }, { "key": "sharebookEmail", "value": "raffacabofrio@gmail.com" }, { "key": "sharebookPassword", "value": "123456" }]' 18 | # TODO: Add a job for production environment which must get username and password from github secrets 19 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Service/Meetup/Dto/SymplaDTO.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using Newtonsoft.Json.Serialization; 3 | using System.Collections.Generic; 4 | 5 | namespace ShareBook.Service.Dto; 6 | 7 | // 24/11/2024 - Paramos de integrar com sympla. Agora carregamos a lista de meetups apenas do YOUTUBE. 8 | public class SymplaDto 9 | { 10 | public string Status { get; set; } 11 | public string Code { get; set; } 12 | public string Message { get; set; } 13 | public List Data { get; set; } 14 | } 15 | public class SymplaEvent 16 | { 17 | public int Id { get; set; } 18 | [JsonProperty("start_date")] 19 | public string StartDate { get; set; } 20 | public string Name { get; set; } 21 | public string Detail { get; set; } 22 | public string Image { get; set; } 23 | public string Url { get; set; } 24 | } 25 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Repository/Mapping/JobHistoryMap.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using Microsoft.EntityFrameworkCore.Metadata.Builders; 3 | using ShareBook.Domain; 4 | 5 | namespace ShareBook.Repository.Mapping 6 | { 7 | public class JobHistoryMap : IEntityTypeConfiguration 8 | { 9 | public void Configure(EntityTypeBuilder entityBuilder) 10 | { 11 | entityBuilder.HasKey(t => t.Id); 12 | 13 | entityBuilder.Property(t => t.JobName) 14 | .HasMaxLength(200) 15 | .IsRequired(); 16 | 17 | entityBuilder.Property(t => t.LastResult) 18 | .HasMaxLength(200); 19 | 20 | entityBuilder.Property(t => t.Details) 21 | .HasMaxLength(4000); // Limite compatível com todos os bancos 22 | 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Domain/JobHistory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using ShareBook.Domain.Common; 4 | using ShareBook.Domain.Enums; 5 | 6 | namespace ShareBook.Domain 7 | { 8 | public class JobHistory : BaseEntity 9 | { 10 | public string JobName { get; set; } 11 | public bool IsSuccess { get; set; } 12 | 13 | // Alguns jobs precisam saber de onde terminou o ultimo ciclo pra continuar a partir daí. 14 | // permitindo dividir um GRANDE PROCESSAMENTO em vários PEQUENOS. 15 | public string LastResult { get; set; } 16 | public string Details { get; set; } 17 | 18 | // Precisamos monitorar a duração dos nossos jobs, levando em consideração 19 | // que estamos numa hospedagem compartilhada e nosso limite é de 300 segundos. 20 | public double TimeSpentSeconds { get; set; } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Domain/BookUser.cs: -------------------------------------------------------------------------------- 1 | using ShareBook.Domain.Common; 2 | using ShareBook.Domain.Enums; 3 | using System; 4 | 5 | namespace ShareBook.Domain 6 | { 7 | public class BookUser : BaseEntity 8 | { 9 | public Guid BookId { get; set; } 10 | public Book Book { get; set; } 11 | public User User { get; set; } 12 | public Guid UserId { get; set; } 13 | public string NickName { get; set; } 14 | public DonationStatus Status { get; private set; } = DonationStatus.WaitingAction; 15 | public string Note { get; set; } // motivo do doador ter escolhido. 16 | public string Reason { get; set; } // justificativa do interessado. 17 | 18 | public void UpdateBookUser(DonationStatus status, string note) 19 | { 20 | this.Status = status; 21 | this.Note = note; 22 | } 23 | 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Repository/Repository/AccessHistory/AccessHistoryRepository.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | 3 | using ShareBook.Domain; 4 | 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Linq; 8 | using System.Threading.Tasks; 9 | 10 | namespace ShareBook.Repository { 11 | public class AccessHistoryRepository : RepositoryGeneric, IAccessHistoryRepository { 12 | public AccessHistoryRepository(ApplicationDbContext context) : base(context) { } 13 | public async Task> GetWhoAccessedMyProfileAsync(Guid userId) { 14 | if (userId.Equals(null)) return null; 15 | 16 | var list = from u in _context.AccessHistories 17 | where (u.UserId.Equals(userId)) 18 | select u; 19 | 20 | return await list.ToListAsync(); 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /ShareBook/ShareBook.Service/Email/Templates/ParentAprovedNotifyUser.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Consentimento dos pais 7 | 8 | 9 |

10 | Olá! Notícia boa! Seu acesso foi liberado pelos seus pais. Agora você pode entrar no sharebook para doar e ganhar livros. Aproveite! =) 11 |

12 |

13 | entrar no Sharebook 14 |

15 | 16 |

17 | Atenciosamente,

18 | 19 | Sharebook team - Compartilhando conhecimento
20 | Siga-nos no instagram: 21 | @sharebook.com.br 22 |

23 | 24 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Repository/Mapping/LogEntryMap.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using Microsoft.EntityFrameworkCore.Metadata.Builders; 3 | using ShareBook.Domain; 4 | 5 | namespace ShareBook.Repository.Mapping 6 | { 7 | public class LogEntryMap : IEntityTypeConfiguration 8 | { 9 | public void Configure(EntityTypeBuilder entityBuilder) 10 | { 11 | entityBuilder.HasKey(t => t.Id); 12 | entityBuilder.Property(t => t.UserId); 13 | entityBuilder.Property(t => t.EntityName).HasMaxLength(64); 14 | entityBuilder.Property(t => t.EntityId); 15 | entityBuilder.Property(t => t.Operation); 16 | entityBuilder.Property(t => t.LogDateTime); 17 | entityBuilder.Property(t => t.ValuesChanges); 18 | 19 | entityBuilder.HasIndex("EntityName", "EntityId"); 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /ShareBook/ShareBook.Service/Email/Templates/WaitingApprovalTemplate.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Aguarde aprovação - Sharebook 6 | 7 | 8 |

9 | Olá {User.Name}, nossos agradecimentos por doar na plataforma Sharebook e compartilhar conhecimento! 10 |

11 |

12 | Nossa equipe já está trabalhando na aprovação do {Title} e logo estará na nossa vitrine para que outras pessoas possam visualizar. 13 |

14 |

15 | Solicitamos que aguarde o e-mail de confirmação dessa aprovação =) 16 |

17 | Detalhes do livro: 18 |
    19 |
  • Livro: {Title}
  • 20 |
  • Autor: {Author}
  • 21 |
22 | 23 |

Equipe Sharebook

24 | 25 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Api/ViewModels/UpdateBookVM.cs: -------------------------------------------------------------------------------- 1 | using ShareBook.Domain.Enums; 2 | using System; 3 | 4 | namespace ShareBook.Api.ViewModels 5 | { 6 | public class UpdateBookVM : BaseViewModel 7 | { 8 | public string Title { get; set; } 9 | 10 | public string Author { get; set; } 11 | 12 | public Guid CategoryId { get; set; } 13 | 14 | public Guid UserId { get; set; } 15 | 16 | public Guid UserIdFacilitator { get; set; } 17 | 18 | public bool Approved { get; set; } 19 | 20 | public string ImageName { get; set; } 21 | 22 | public byte[] ImageBytes { get; set; } 23 | 24 | public string Synopsis { get; set; } 25 | 26 | public FreightOption FreightOption { get; set; } 27 | public string Type { get; set; } 28 | public string EBookDownloadLink { get; set; } 29 | public string EBookPdfFile { get; set; } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Api/web.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Domain/Validators/MeetupValidator.cs: -------------------------------------------------------------------------------- 1 | using FluentValidation; 2 | 3 | namespace ShareBook.Domain.Validators 4 | { 5 | public class MeetupValidator : AbstractValidator 6 | { 7 | public MeetupValidator() 8 | { 9 | RuleFor(m => m.Title) 10 | .NotEmpty() 11 | .WithMessage("A propriedade: {propertyName} é obrigatória!"); 12 | 13 | RuleFor(m => m.StartDate) 14 | .NotEmpty() 15 | .WithMessage("A propriedade: {propertyName} é obrigatória!"); 16 | 17 | RuleFor(m => m.SymplaEventUrl) 18 | .NotEmpty() 19 | .WithMessage("A propriedade: {propertyName} é obrigatória!"); 20 | 21 | RuleFor(m => m.SymplaEventId) 22 | .NotEmpty() 23 | .WithMessage("A propriedade: {propertyName} é obrigatória!"); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Repository/UoW/UnitOfWork.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection.Metadata.Ecma335; 3 | using System.Threading.Tasks; 4 | 5 | namespace ShareBook.Repository.UoW 6 | { 7 | public class UnitOfWork : IUnitOfWork 8 | { 9 | private readonly ApplicationDbContext _context; 10 | 11 | public UnitOfWork(ApplicationDbContext context) => _context = context; 12 | 13 | public async Task BeginTransactionAsync() => await _context.Database.BeginTransactionAsync(); 14 | public async Task CommitAsync() => await _context.Database.CommitTransactionAsync(); 15 | public async Task RollbackAsync() => await _context.Database.RollbackTransactionAsync(); 16 | public async ValueTask DisposeAsync() 17 | { 18 | if (_context?.Database?.CurrentTransaction != null) 19 | await _context.Database.CurrentTransaction.RollbackAsync(); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Service/Email/IEmailService.cs: -------------------------------------------------------------------------------- 1 | using ShareBook.Domain; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | 5 | namespace ShareBook.Service 6 | { 7 | public interface IEmailService 8 | { 9 | Task SendToAdminsAsync(string messageText, string subject); 10 | Task SendAsync(string emailRecipient, string nameRecipient, string messageText, string subject); 11 | Task SendAsync(string emailRecipient, string nameRecipient, string messageText, string subject, bool copyAdmins, bool highPriority); 12 | Task SendSmtpAsync(string emailRecipient, string nameRecipient, string messageText, string subject, bool copyAdmins); 13 | Task TestAsync(string email, string name); 14 | 15 | Task> ProcessBounceMessagesAsync(); 16 | Task> GetBouncesAsync(string email); 17 | Task IsBounceAsync(string email); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Service/Email/Templates/BookDonatedTemplate.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Parabéns você foi selecionado para receber o livro - Sharebook 6 | 7 | 8 |

Parabéns você foi selecionado para receber o livro {Book.Title}

9 |

10 | Olá {User.Name}, 11 |

12 |

13 | Veja mais informações abaixo sobre o livro doado: 14 |

15 | 16 |
    17 |
  • Livro: {Book.Title}
  • 18 |
  • Doador: {Book.User.Name}
  • 19 |
  • Linkedin Doador: {Book.User.Linkedin}
  • 20 |
  • Telefone Doador: {Book.User.Phone}
  • 21 |
  • Email Doador: {Book.User.Email}
  • 22 |
23 | 24 |

Sharebook

25 | 26 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Helper/Extensions/EnumeratorExtension.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | 4 | namespace ShareBook.Helper.Extensions 5 | { 6 | public static class EnumeratorExtension 7 | { 8 | public static string Description(this Enum value) 9 | { 10 | // get attributes 11 | var field = value.GetType().GetField(value.ToString()); 12 | var attributes = field.GetCustomAttributes(false); 13 | 14 | // Description is in a hidden Attribute class called DisplayAttribute 15 | // Not to be confused with DisplayNameAttribute 16 | dynamic displayAttribute = null; 17 | 18 | if (attributes.Any()) 19 | { 20 | displayAttribute = attributes.ElementAt(0); 21 | } 22 | 23 | // return description 24 | return displayAttribute?.Description ?? "Description Not Found"; 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Helper/Mapper/Mapper.cs: -------------------------------------------------------------------------------- 1 | using AutoMapper; 2 | using System; 3 | 4 | namespace ShareBook.Helper.Mapper 5 | { 6 | public static class Mapper 7 | { 8 | //public static TDestination Map(object source) 9 | //{ 10 | // return AutoMapper.Mapper.Map(source); 11 | //} 12 | 13 | //public static TDestination Map(TSource source) 14 | //{ 15 | // return AutoMapper.Mapper.Map(source); 16 | //} 17 | 18 | //public static TDestination Map(TSource source, TDestination destination) 19 | //{ 20 | // return AutoMapper.Mapper.Map(source, destination); 21 | //} 22 | 23 | //public static void Initialize(Action action) 24 | //{ 25 | // AutoMapper.Mapper.Initialize(action); 26 | //} 27 | } 28 | } -------------------------------------------------------------------------------- /ShareBook/ShareBook.Domain/Validators/BookUserValidator.cs: -------------------------------------------------------------------------------- 1 | using FluentValidation; 2 | using ShareBook.Domain.Enums; 3 | 4 | namespace ShareBook.Domain.Validators 5 | { 6 | public class BookUserValidator : AbstractValidator 7 | { 8 | #region Messages 9 | public const string Book = "Livro é obrigatório"; 10 | public const string Requester = "Solicitante do livro é obrigatório"; 11 | public const string RequesterReason = "Justificativa do solicitante é obrigatória"; 12 | #endregion 13 | 14 | public BookUserValidator() 15 | { 16 | RuleFor(b => b.BookId) 17 | .NotEmpty() 18 | .WithMessage(Book); 19 | 20 | RuleFor(b => b.UserId) 21 | .NotEmpty() 22 | .WithMessage(Requester); 23 | 24 | RuleFor(b => b.Reason) 25 | .NotEmpty() 26 | .WithMessage(RequesterReason); 27 | 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Helper/Extensions/AssemblyExtension.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Reflection; 4 | 5 | namespace ShareBook.Helper.Extensions 6 | { 7 | public static class AssemblyExtension 8 | { 9 | public static DateTime GetLinkerTime(this Assembly ass) 10 | { 11 | const string BuildVersionPrefix = "+build"; 12 | 13 | var attribute = ass.GetCustomAttribute(); 14 | if (attribute?.InformationalVersion != null) 15 | { 16 | var value = attribute.InformationalVersion; 17 | var index = value.IndexOf(BuildVersionPrefix); 18 | if (index > 0) 19 | { 20 | value = value[(index + BuildVersionPrefix.Length)..]; 21 | return DateTime.ParseExact(value, "yyyy-MM-ddTHH:mm:ss:fffZ", CultureInfo.InvariantCulture); 22 | } 23 | } 24 | return default; 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Test.Integration/Setup/ShareBookTestsFixture.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | using ShareBook.Repository; 3 | 4 | namespace ShareBook.Test.Integration.Setup; 5 | 6 | 7 | [CollectionDefinition(nameof(ShareBookTestsFixture))] 8 | public class ShareBookTestsFixtureCollection : ICollectionFixture 9 | { } 10 | 11 | public class ShareBookTestsFixture 12 | { 13 | public ShareBookWebAppFactory ShareBookWebAppFactory { get; } = new ShareBookWebAppFactory(); 14 | public HttpClient ShareBookApiClient { get; init; } 15 | public ApplicationDbContext ApplicationDbContext { get; init; } 16 | 17 | public ShareBookTestsFixture() 18 | { 19 | ShareBookApiClient = ShareBookWebAppFactory.CreateClient(); 20 | ApplicationDbContext = ShareBookWebAppFactory.Services.GetRequiredService(); 21 | 22 | // Seed data 23 | var sharebookSeeder = new ShareBookSeeder(ApplicationDbContext); 24 | sharebookSeeder.Seed(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Repository/ShareBook.Infra.Data.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net8.0 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Service/Email/Templates/BookApprovedTemplate.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Livro aprovado - Sharebook 6 | 7 | 8 |

9 | Olá {User.Name} 10 |

11 |

12 | O livro {Book.Title} foi aprovado e já está na nossa vitrine para doação. 13 |

14 | Detalhes do livro: 15 |
    16 |
  • Livro: {Book.Title}
  • 17 |
  • Autor: {Book.Author}
  • 18 |
19 | 20 |

21 | A data de escolha do vencedor será no dia {ChooseDate}.
22 | Pedimos para que reserve um tempo neste dia para escolher o ganhador da sua doação.
23 | Lembre-se: esse passo é fundamental para uma melhor experiência de todos!
24 | Os interessados estarão anciosos pela sua escolha. Por isso, não perca a data! 25 |

26 | 27 |

Equipe Sharebook

28 | 29 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Service/BookUser/IBookUsersEmailService.cs: -------------------------------------------------------------------------------- 1 | using ShareBook.Domain; 2 | using ShareBook.Domain.DTOs; 3 | using System.Collections.Generic; 4 | using System.Threading.Tasks; 5 | 6 | namespace ShareBook.Service 7 | { 8 | public interface IBookUsersEmailService 9 | { 10 | Task SendEmailBookDonatedAsync(BookUser bookUser); 11 | 12 | Task SendEmailBookDonatedNotifyDonorAsync(Book book, User winner); 13 | 14 | Task SendEmailBookDonorAsync(BookUser bookUser, Book bookRequested); 15 | 16 | Task SendEmailBookInterestedAsync(BookUser bookUser, Book book); 17 | 18 | Task SendEmailDonationDeclinedAsync(Book book, BookUser bookUserWinner, List bookUsersDeclined); 19 | 20 | Task SendEmailDonationCanceledAsync(Book book, List bookUsers); 21 | 22 | Task SendEmailBookCanceledToAdminsAndDonorAsync(BookCancelationDTO dto); 23 | 24 | Task SendEmailTrackingNumberInformedAsync(BookUser bookUserWinner, Book book); 25 | 26 | Task SendEmailMaxRequestsAsync(Book bookRequested); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Service/Email/Templates/BookNoticeDonorTemplate.html: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Seu livro foi solicitado - Sharebook 6 | 7 | 8 |

Seu livro foi solicitado - Sharebook

9 |

10 | Olá {Donor.Name}, 11 |

12 |
13 |

14 | Seu livro "{Donor.BookTitle}" foi solicitado. 15 |

16 | 17 | {HtmlTable} 18 | 19 |

20 | Atenção! Este é apenas uma aviso que alguém está interessado no seu livro.
21 | É necessário no entanto aguardar até a data de {Donor.ChooseDate} para escolher o ganhador. Blz? 22 |

23 |

24 | Atenciosamente, 25 |

26 |

27 | Sharebook - Compartilhando conhecimento
28 | Siga-nos no instagram: 29 | @sharebook.com.br 30 |

31 | 32 | 33 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Api/Filters/ValidateModelStateFilterAttribute.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using Microsoft.AspNetCore.Mvc.Filters; 3 | using ShareBook.Domain.Common; 4 | using System.Linq; 5 | 6 | namespace ShareBook.Api.Filters 7 | { 8 | public class ValidateModelStateFilterAttribute : ActionFilterAttribute 9 | { 10 | public override void OnActionExecuting(ActionExecutingContext context) 11 | { 12 | if (!context.ModelState.IsValid) 13 | { 14 | var errors = context.ModelState.Values.Where(v => v.Errors.Count > 0) 15 | .SelectMany(v => v.Errors) 16 | .Select(v => v.ErrorMessage) 17 | .ToList(); 18 | 19 | var response = new Result(); 20 | foreach (var error in errors) 21 | response.Messages.Add(error); 22 | 23 | context.Result = new JsonResult(response) 24 | { 25 | StatusCode = 400 26 | }; 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Test.Unit/Services/RecaptchaServiceTests.cs: -------------------------------------------------------------------------------- 1 | using ShareBook.Domain.Common; 2 | using ShareBook.Service.Recaptcha; 3 | using Xunit; 4 | 5 | namespace ShareBook.Test.Unit.Services 6 | { 7 | public class RecaptchaServiceTests 8 | { 9 | private readonly IRecaptchaService _recaptchaService = new RecaptchaService(); 10 | private const string validRecaptcha = "asdaasdjasodaj7i364yubki23y728374234b2jk34h2347i26348724yh2bjhk34g2j34t273842384iuh2h4j234g2j34t27834bjh"; 11 | private const string invalidRecaptcha = "123456"; 12 | public RecaptchaServiceTests() { } 13 | 14 | 15 | [Fact] 16 | public void ValidRecaptcha() 17 | { 18 | Result result = _recaptchaService.SimpleValidationRecaptcha(validRecaptcha); 19 | Assert.True(result.Success); 20 | } 21 | 22 | [Fact] 23 | public void InvalidRecaptcha() 24 | { 25 | Result result = _recaptchaService.SimpleValidationRecaptcha(invalidRecaptcha); 26 | Assert.False(result.Success); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Service/AccessHistory/AccessHistoryService.cs: -------------------------------------------------------------------------------- 1 | using FluentValidation; 2 | 3 | using ShareBook.Domain; 4 | using ShareBook.Domain.Enums; 5 | using ShareBook.Repository; 6 | using ShareBook.Repository.UoW; 7 | using ShareBook.Service.Generic; 8 | 9 | using System.Threading.Tasks; 10 | 11 | namespace ShareBook.Service { 12 | public class AccessHistoryService : BaseService, IAccessHistoryService { 13 | private readonly IAccessHistoryRepository _accessHistoryRepository; 14 | 15 | public AccessHistoryService(IAccessHistoryRepository repository, 16 | IUnitOfWork unitOfWork, 17 | IValidator validator) : base(repository, unitOfWork, validator) 18 | { 19 | _accessHistoryRepository = repository; 20 | } 21 | 22 | public async Task InsertVisitorAsync(User user, User visitor, VisitorProfile profile) { 23 | var visitorProfile = new AccessHistory(user.Id, visitor.Name, profile); 24 | 25 | await _accessHistoryRepository.InsertAsync(visitorProfile); 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /devops/.dockerignore: -------------------------------------------------------------------------------- 1 | # Build outputs 2 | **/bin/ 3 | **/obj/ 4 | **/out/ 5 | **/publish/ 6 | 7 | # Visual Studio 8 | .vs/ 9 | *.user 10 | *.suo 11 | *.userosscache 12 | *.sln.docstates 13 | 14 | # JetBrains 15 | .idea/ 16 | *.sln.iml 17 | 18 | # Test results 19 | TestResults/ 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | build/ 27 | bld/ 28 | [Bb]in/ 29 | [Oo]bj/ 30 | [Oo]ut/ 31 | msbuild.log 32 | msbuild.err 33 | msbuild.wrn 34 | 35 | # Node.js (if used for frontend) 36 | node_modules/ 37 | npm-debug.log* 38 | 39 | # Docker 40 | Dockerfile* 41 | .dockerignore 42 | docker-compose*.yml 43 | 44 | # DevOps 45 | devops/ 46 | 47 | # Git 48 | .git/ 49 | .gitignore 50 | 51 | # Documentation 52 | *.md 53 | README* 54 | 55 | # Logs 56 | logs/ 57 | *.log 58 | 59 | # User-specific files 60 | *.rsuser 61 | *.userprefs 62 | launchSettings.json 63 | 64 | # Coverage reports 65 | coverage/ 66 | *.cobertura.xml 67 | 68 | # Database files 69 | *.db 70 | *.sqlite* 71 | 72 | # Temporary files 73 | [Tt]mp/ 74 | [Tt]emp/ -------------------------------------------------------------------------------- /ShareBook/ShareBook.Service/Email/Templates/RequestParentAproval.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Consentimento dos pais 7 | 8 | 9 |

10 | Olá! O usuário {UserName}, menor de idade, se cadastrou em nosso app Sharebook e informou que você é o responsável. Precisamos do seu consentimento para liberar o acesso. 11 |

12 |

13 | Sharebook é um app livre e gratuito para doação de livros. Temos todo o cuidado com a segurança e privacidade dos nossos usuários. 14 |

15 |

16 | Para liberar o acesso por favor use esse link: {AprovalLink} 17 |

18 | 19 |

20 | Atenciosamente,

21 | 22 | Sharebook team - Compartilhando conhecimento
23 | Siga-nos no instagram: 24 | @sharebook.com.br 25 |

26 | 27 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Api/Services/RollbarConfigurator.cs: -------------------------------------------------------------------------------- 1 | using Rollbar; 2 | using System; 3 | 4 | namespace ShareBook.Api.Services 5 | { 6 | public static class RollbarConfigurator 7 | { 8 | public static bool IsActive { get; private set; } 9 | 10 | public static void Configure(string environment, string isActive, string token, string logLevel) 11 | { 12 | if (string.IsNullOrEmpty(environment) || 13 | string.IsNullOrEmpty(isActive) || 14 | string.IsNullOrEmpty(token)) 15 | return; 16 | 17 | var result = ErrorLevel.TryParse(logLevel, out ErrorLevel logLevelEnum); 18 | 19 | if (!result) 20 | throw new Exception("Rollbar invalid logLevel: " + logLevel); 21 | 22 | RollbarLocator.RollbarInstance.Configure(new RollbarConfig(accessToken: token) { Environment = environment, LogLevel = logLevelEnum, AccessToken = token }); 23 | RollbarLocator.RollbarInstance.Info($"Rollbar is configured properly in {environment} environment."); 24 | 25 | IsActive = true; 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /ShareBook/ShareBook.Domain/Common/Result.cs: -------------------------------------------------------------------------------- 1 | using FluentValidation.Results; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace ShareBook.Domain.Common 6 | { 7 | public class Result : Result 8 | { 9 | public Result() : base(null) { } 10 | public Result(string SuccessMessage) : base(null) 11 | { 12 | this.SuccessMessage = SuccessMessage; 13 | } 14 | } 15 | 16 | public class Result where T : class 17 | { 18 | public Result(T value) : this(null, value) { } 19 | public Result(ValidationResult validationResult) : this(validationResult, null) { } 20 | public Result(ValidationResult validationResult, T value) 21 | { 22 | Messages = validationResult?.Errors.Select(x => x.ErrorMessage).ToList() ?? new List(); 23 | Value = value; 24 | } 25 | 26 | public T Value { get; set; } 27 | public List Messages { get; } 28 | public string SuccessMessage { get; set; } 29 | 30 | public bool Success { get { return Messages.Count == 0; } } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Domain/DTOs/RegisterUserDTO.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace ShareBook.Domain.DTOs 3 | { 4 | public class RegisterUserDTO 5 | { 6 | public string Name { get; set; } 7 | 8 | public string Email { get; set; } 9 | 10 | public string Street { get; set; } 11 | 12 | public string Number { get; set; } 13 | 14 | public string Complement { get; set; } 15 | 16 | public string Neighborhood { get; set; } 17 | 18 | public string PostalCode { get; set; } 19 | 20 | public string City { get; set; } 21 | 22 | public string State { get; set; } 23 | 24 | public string Country { get; set; } 25 | 26 | public string Linkedin { get; set; } 27 | 28 | public string Instagram { get; set; } 29 | 30 | public string Phone { get; set; } 31 | 32 | public string Password { get; set; } 33 | 34 | public bool AllowSendingEmail { get; set; } = true; 35 | 36 | public int Age { get; set; } 37 | 38 | public string ParentEmail { get; set; } 39 | public string RecaptchaReactive { get; set; } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /devops/Dockerfile: -------------------------------------------------------------------------------- 1 | # Build stage 2 | FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build 3 | WORKDIR /app 4 | 5 | # Copy source code 6 | COPY ShareBook/ ./ShareBook/ 7 | 8 | # Restore and build in one step to avoid cache issues 9 | RUN dotnet publish ShareBook/ShareBook.Api/ShareBook.Api.csproj -c Release -o /app/publish --verbosity normal 10 | 11 | # Runtime stage 12 | FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS runtime 13 | 14 | # Instala curl - necessário pro health check funcionar 15 | RUN apt-get update && apt-get install -y curl 16 | 17 | WORKDIR /app 18 | 19 | # Copy published files 20 | COPY --from=build /app/publish . 21 | 22 | # força o ASP.NET Core a escutar na porta 8080 23 | ENV ASPNETCORE_URLS=http://0.0.0.0:8080 24 | 25 | # Create non-root user 26 | RUN groupadd -r appuser && useradd -r -g appuser appuser 27 | RUN chown -R appuser:appuser /app 28 | USER appuser 29 | 30 | # Expose port 31 | EXPOSE 8080 32 | 33 | HEALTHCHECK --interval=30s --timeout=3s --start-period=30s \ 34 | CMD curl -fsS http://127.0.0.1:8080/api/Operations/Ping || exit 1 35 | 36 | # Set entry point 37 | ENTRYPOINT ["dotnet", "ShareBook.Api.dll"] -------------------------------------------------------------------------------- /ShareBook/ShareBook.Domain/Enums/BookStatus.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | using System.Text.Json.Serialization; 3 | 4 | namespace ShareBook.Domain.Enums 5 | { 6 | [JsonConverter(typeof(JsonStringEnumConverter))] 7 | public enum BookStatus 8 | { 9 | [Description("Aguardando aprovação")] 10 | WaitingApproval,// Status inicial 11 | 12 | [Description("Disponível")] 13 | Available,// Status é usado quando o admin aprova o livro 14 | 15 | [Description("Aguardando decisão do doador")] 16 | AwaitingDonorDecision,// Status para quando já expirou a qt de dias que o livro pode ficar na vitrine tendo pessoas ja interessadas em ganha-lo e o doador ainda nao escolheu o ganhador 17 | 18 | [Description("Aguardando envio")] 19 | WaitingSend,// Status é usado a partir do momento que o doador escolhe um ganhador 20 | 21 | [Description("Enviado")] 22 | Sent,// Status para quando o doador envia o livro 23 | 24 | [Description("Recebido")] 25 | Received,// Status para quando o ganhador recebe o livro 26 | 27 | [Description("Cancelado")] 28 | Canceled 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Repository/Mapping/BookUserMap.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using Microsoft.EntityFrameworkCore.Metadata.Builders; 3 | using ShareBook.Domain; 4 | 5 | namespace ShareBook.Repository.Mapping 6 | { 7 | public class BookUserMap : IEntityTypeConfiguration 8 | { 9 | public void Configure(EntityTypeBuilder entityBuilder) 10 | { 11 | 12 | entityBuilder 13 | .HasKey(bu => new { bu.Id, bu.BookId, bu.UserId }); 14 | 15 | entityBuilder 16 | .HasOne(bu => bu.Book) 17 | .WithMany(b => b.BookUsers) 18 | .HasForeignKey(bu => bu.BookId); 19 | 20 | entityBuilder 21 | .HasOne(bu => bu.User) 22 | .WithMany(u => u.BookUsers) 23 | .HasForeignKey(bu => bu.UserId); 24 | 25 | entityBuilder.Property(bu => bu.Note) 26 | .HasMaxLength(2000); 27 | 28 | entityBuilder.Property(bu => bu.Reason) 29 | .HasMaxLength(2000); 30 | 31 | entityBuilder.Property(bu => bu.NickName) 32 | .HasMaxLength(64); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Api/ViewModels/UserVM.cs: -------------------------------------------------------------------------------- 1 | using ShareBook.Domain; 2 | using System; 3 | 4 | namespace ShareBook.Api.ViewModels 5 | { 6 | public class UserVM : BaseViewModel 7 | { 8 | 9 | public string Name { get; set; } 10 | 11 | public string Email { get; set; } 12 | 13 | public string Linkedin { get; set; } 14 | 15 | public string Instagram { get; set; } 16 | 17 | public string Phone { get; set; } 18 | 19 | public Address Address { get; set; } 20 | 21 | public bool AllowSendingEmail { get; set; } 22 | } 23 | 24 | public class UserFacilitatorVM 25 | { 26 | 27 | public Guid Id { get; set; } 28 | 29 | public string Name { get; set; } 30 | 31 | public string Email { get; set; } 32 | 33 | public string Linkedin { get; set; } 34 | 35 | public string Instagram { get; set; } 36 | 37 | public string Phone { get; set; } 38 | 39 | public Address Address { get; set; } 40 | } 41 | 42 | public class MainUsersVM 43 | { 44 | public UserVM Donor { get; set; } 45 | public UserVM Facilitator { get; set; } 46 | public UserVM Winner { get; set; } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Repository/Mapping/AddressMap.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using Microsoft.EntityFrameworkCore.Metadata.Builders; 3 | using ShareBook.Domain; 4 | 5 | namespace ShareBook.Repository.Mapping 6 | { 7 | public class AddressMap : IEntityTypeConfiguration
8 | { 9 | public void Configure(EntityTypeBuilder
entityBuilder) 10 | { 11 | entityBuilder.Property(t => t.PostalCode) 12 | .HasMaxLength(15); 13 | 14 | entityBuilder.Property(t => t.Neighborhood) 15 | .HasMaxLength(50); 16 | 17 | entityBuilder.Property(t => t.Complement) 18 | .HasMaxLength(50); 19 | 20 | entityBuilder.Property(t => t.Country) 21 | .HasMaxLength(50); 22 | 23 | entityBuilder.Property(t => t.City) 24 | .HasMaxLength(50); 25 | 26 | entityBuilder.Property(t => t.State) 27 | .HasMaxLength(30); 28 | 29 | entityBuilder.Property(t => t.Street) 30 | .HasMaxLength(80); 31 | 32 | entityBuilder.Property(t => t.Number) 33 | .HasMaxLength(10); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Api/Controllers/ContactUsController.cs: -------------------------------------------------------------------------------- 1 | using AutoMapper; 2 | using Microsoft.AspNetCore.Cors; 3 | using Microsoft.AspNetCore.Mvc; 4 | using ShareBook.Api.ViewModels; 5 | using ShareBook.Domain; 6 | using ShareBook.Domain.Common; 7 | using ShareBook.Service; 8 | using System.Threading.Tasks; 9 | 10 | namespace ShareBook.Api.Controllers 11 | { 12 | [Route("api/[controller]")] 13 | [EnableCors("AllowAllHeaders")] 14 | public class ContactUsController : ControllerBase 15 | { 16 | private readonly IContactUsService _contactUsService; 17 | private readonly IMapper _mapper; 18 | 19 | public ContactUsController(IContactUsService contactUsService, 20 | IMapper mapper) 21 | { 22 | _contactUsService = contactUsService; 23 | _mapper = mapper; 24 | } 25 | 26 | [HttpPost("SendMessage")] 27 | public async Task> SendMessageAsync([FromBody]ContactUsVM contactUsVM) 28 | { 29 | var contactUS = _mapper.Map(contactUsVM); 30 | 31 | return await _contactUsService.SendContactUsAsync(contactUS, contactUsVM?.RecaptchaReactive); 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /ShareBook/ShareBook.Domain/Exceptions/SharebookException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace ShareBook.Domain.Exceptions 5 | { 6 | public class ShareBookException : Exception 7 | { 8 | public static Dictionary ErrorMessages = new Dictionary() 9 | { 10 | { Error.NotAuthorized, "O usuário precisa estar logado para efetuar essa ação." }, 11 | { Error.Forbidden, "Usuário não tem as permissões necessárias para efetuar esta ação." }, 12 | { Error.NotFound, "Entidade não encontrada. Por favor, verifique." } 13 | }; 14 | 15 | public enum Error 16 | { 17 | BadRequest = 400, 18 | NotAuthorized = 401, 19 | Forbidden = 403, 20 | NotFound = 404 21 | } 22 | 23 | public Error ErrorType { get; set; } 24 | 25 | public ShareBookException(string message) : this(Error.BadRequest, message) { } 26 | public ShareBookException(Error error) : this(error, ErrorMessages[error]) { } 27 | public ShareBookException(Error error, string message) : base(message) 28 | { 29 | ErrorType = error; 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Test.Integration/ShareBook.Test.Integration.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | enable 7 | 8 | false 9 | true 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Domain/Validators/ContactUsValidator.cs: -------------------------------------------------------------------------------- 1 | using FluentValidation; 2 | 3 | namespace ShareBook.Domain.Validators 4 | { 5 | public class ContactUsValidator : AbstractValidator 6 | { 7 | #region Messages 8 | public const string NAME_REQUIRED = "Nome é obrigatório"; 9 | public const string EMAIL_REQUIRED = "Email é obrigatório"; 10 | public const string EMAIL_FORMAT = "O formato do email está inválido"; 11 | public const string PHONE_REQUIRED = "Telefone é obrigatório"; 12 | public const string MESSAGE_REQUIRED = "Mensagem é obrigatória"; 13 | #endregion 14 | 15 | public ContactUsValidator() 16 | { 17 | RuleFor(c => c.Name) 18 | .NotEmpty() 19 | .WithMessage(NAME_REQUIRED); 20 | 21 | RuleFor(c => c.Email) 22 | .EmailAddress() 23 | .WithMessage(EMAIL_FORMAT) 24 | .NotEmpty() 25 | .WithMessage(EMAIL_REQUIRED); 26 | 27 | RuleFor(c => c.Phone) 28 | .NotEmpty() 29 | .WithMessage(PHONE_REQUIRED); 30 | 31 | RuleFor(c => c.Message) 32 | .NotEmpty() 33 | .WithMessage(MESSAGE_REQUIRED); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Service/User/IUserService.cs: -------------------------------------------------------------------------------- 1 | using ShareBook.Domain; 2 | using ShareBook.Domain.Common; 3 | using ShareBook.Domain.DTOs; 4 | using ShareBook.Service.Generic; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Threading.Tasks; 8 | 9 | namespace ShareBook.Service 10 | { 11 | public interface IUserService : IBaseService 12 | { 13 | Task> AuthenticationByEmailAndPasswordAsync(User user); 14 | bool IsValidPassword(User user, string decryptedPass); 15 | new Task> UpdateAsync(User user); 16 | Task> ValidOldPasswordAndChangeUserPasswordAsync(User user, string newPassword); 17 | Task> ChangeUserPasswordAsync(User user, string newPassword); 18 | Task GenerateHashCodePasswordAndSendEmailToUserAsync(string email); 19 | Task ConfirmHashCodePasswordAsync(string hashCodePassword); 20 | IList GetFacilitators(Guid userIdDonator); 21 | IList GetAdmins(); 22 | Task> GetBySolicitedBookCategoryAsync(Guid bookCategoryId); 23 | Task GetStatsAsync(Guid? userId); 24 | Task> InsertAsync(RegisterUserDTO userDto); 25 | Task ParentAprovalAsync(string parentHashCodeAproval); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "build", 6 | "command": "dotnet", 7 | "type": "shell", 8 | "args": [ 9 | "build", 10 | "${workspaceFolder}/ShareBook/ShareBook.Api/ShareBook.Api.csproj", 11 | "/property:GenerateFullPaths=true" 12 | ], 13 | "problemMatcher": "$msCompile" 14 | }, 15 | { 16 | "label": "publish", 17 | "command": "dotnet", 18 | "type": "process", 19 | "args": [ 20 | "publish", 21 | "${workspaceFolder}/ShareBook/ShareBook.Api/ShareBook.Api.csproj", 22 | "/property:GenerateFullPaths=true", 23 | "/consoleloggerparameters:NoSummary" 24 | ], 25 | "problemMatcher": "$msCompile" 26 | }, 27 | { 28 | "label": "watch", 29 | "command": "dotnet", 30 | "type": "process", 31 | "args": [ 32 | "watch", 33 | "run", 34 | "--project", 35 | "${workspaceFolder}/ShareBook/ShareBook.Api/ShareBook.Api.csproj", 36 | ], 37 | "problemMatcher": "$msCompile" 38 | } 39 | ] 40 | } -------------------------------------------------------------------------------- /ShareBook/Sharebook.Jobs/Jobs/6 - MailSupressListUpdate.cs: -------------------------------------------------------------------------------- 1 | using ShareBook.Domain; 2 | using ShareBook.Domain.Enums; 3 | using ShareBook.Repository; 4 | using ShareBook.Service; 5 | using System; 6 | using System.Threading.Tasks; 7 | 8 | namespace Sharebook.Jobs; 9 | 10 | public class MailSupressListUpdate : GenericJob, IJob 11 | { 12 | private readonly IEmailService _emailService; 13 | 14 | public MailSupressListUpdate( 15 | IJobHistoryRepository jobHistoryRepo, 16 | IEmailService emailService) : base(jobHistoryRepo) 17 | { 18 | 19 | JobName = "MailSupressListUpdate"; 20 | Description = @"Atualiza a lista de emails suprimidos. Essa lista serve para manter boa reputação do nosso 21 | mailling. Além de ser um requisito da AWS."; 22 | Interval = Interval.Dayly; 23 | Active = true; 24 | BestTimeToExecute = new TimeSpan(2, 0, 0); 25 | 26 | _emailService = emailService; 27 | } 28 | 29 | public override async Task WorkAsync() 30 | { 31 | var log = await _emailService.ProcessBounceMessagesAsync(); 32 | 33 | return new JobHistory() 34 | { 35 | JobName = JobName, 36 | IsSuccess = true, 37 | Details = String.Join("\n", log) 38 | }; 39 | } 40 | } 41 | 42 | 43 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Service/BookUser/IBookUserService.cs: -------------------------------------------------------------------------------- 1 | using ShareBook.Domain; 2 | using ShareBook.Domain.Common; 3 | using ShareBook.Domain.DTOs; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Threading.Tasks; 7 | 8 | namespace ShareBook.Service 9 | { 10 | public interface IBookUserService 11 | { 12 | Task InsertAsync(Guid bookId, string reason); 13 | 14 | Task> GetGranteeUsersByBookIdAsync(Guid bookId); 15 | 16 | Task> GetRequestersListAsync(Guid bookId); 17 | 18 | Task DonateBookAsync(Guid bookId, Guid userId, string note); 19 | 20 | Task DeniedBookUsersAsync(Guid bookId); 21 | 22 | Task> GetRequestsByUserAsync(int page, int items); 23 | 24 | /// 25 | /// Comunicar os interessados não escolhidos sobre a finalização da doação. e quem ganhou o livro 26 | /// 27 | /// 28 | Task NotifyInterestedAboutBooksWinnerAsync(Guid bookId); 29 | 30 | Task> CancelAsync(BookCancelationDTO dto); 31 | 32 | Task InformTrackingNumberAsync(Guid bookId, string trackingNumber); 33 | Task GetRequestAsync(Guid requestId); 34 | Task CancelRequestAsync(BookUser request); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Service/Email/Templates/BookReceivedTemplate.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Livro Recebido - Sharebook 7 | 8 | 9 |

10 | Olá {User.Name} 11 |

12 |

13 | O livro {Book.Title} foi Recebido. 14 |

15 | Detalhes do livro: 16 |
    17 |
  • Livro: {Book.Title}
  • 18 |
  • Autor: {Book.Author}
  • 19 |
  • Ganhador: {WinnerName}
  • 20 |
21 | 22 |

23 | Parabéns por ter completado sua doação com sucesso. Você fez diferença na vida de uma pessoa.
24 |

25 | 26 |

27 | Atenciosamente,

28 | 29 | Sharebook team - Compartilhando conhecimento
30 | Siga-nos no instagram: 31 | @sharebook.com.br 32 |
33 |
34 | Curta nossa fanpage pra ficar por dentro das novidades:
35 | https://www.linkedin.com/company/sharebook-br/ 36 |

37 | 38 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Service/Meetup/Dto/MeetupParticipantDTO.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace ShareBook.Service.Dto 9 | { 10 | public class MeetupParticipantDto 11 | { 12 | public List Data { get; set; } 13 | public Pagination Pagination { get; set; } 14 | } 15 | 16 | public class Data 17 | { 18 | [JsonProperty("event_id")] 19 | public int EventId { get; set; } 20 | [JsonProperty("first_name")] 21 | public string FirstName { get; set; } 22 | [JsonProperty("last_name")] 23 | public string LastName { get; set; } 24 | public string Email { get; set; } 25 | } 26 | 27 | public class Pagination 28 | { 29 | [JsonProperty("has_next")] 30 | public bool HasNext { get; set; } 31 | [JsonProperty("hast_prev")] 32 | public bool HasPrev { get; set; } 33 | public int Quantity { get; set; } 34 | public int Offset { get; set; } 35 | public int Page { get; set; } 36 | [JsonProperty("page_size")] 37 | public int PageSize { get; set; } 38 | [JsonProperty("total_page")] 39 | public int TotalPage { get; set; } 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Service/Email/Templates/BookNoticeInterestedTemplate.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Sharebook - Você solicitou um livro 7 | 8 | 9 |

Sharebook - Você solicitou um livro

10 | 11 |

12 | Olá {NameInterested}, 13 |

14 |
15 |

16 | Você solicitou o livro {NameBook}.
17 | Aguarde até o dia {ChooseDate} que será anunciado o ganhador. Boa sorte! 18 |

19 |
20 |

21 | Em caso de dúvidas fale com o Facilitador {NameFacilitator}. 22 |

23 |
    24 |
  • Whatsapp: {PhoneFacilitator}
  • 25 |
  • Email: {EmailFacilitator}
  • 26 |
  • LinkedIn: {LinkedinFacilitator}
  • 27 |
28 |
29 | 30 |

Atenciosamente, Sharebook Team

31 |
32 |

33 | Sharebook - Compartilhando conhecimento 34 |
35 | Siga-nos no instagram: 36 | @sharebook.com.br 37 |

38 | 39 | 40 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Service/Email/Templates/BookCanceledNoticeUsersTemplate.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Resultado da doação do livro {book.Title} 7 | 8 | 9 | 10 | 11 |

12 | Olá,

13 | 14 | Aqui é o Sharebot do time Sharebook. Blz?

15 | 16 | Obrigado por ter demonstrado interesse no livro "{book.Title}". Mas infelizmente você não foi escolhido(a) como ganhador(a). =(

17 | 18 | Por favor não desanime. Toda semana entram muitos livros novos e certamente um dia vc vai ganhar o seu. Ou até mesmo esse livro pode ser doado de novo. Blz? 19 |
20 |

21 | 22 |

23 | Atenciosamente,

24 | 25 | Sharebook team - Compartilhando conhecimento
26 | Siga-nos no instagram: 27 | @sharebook.com.br 28 |
29 |
30 | Curta nossa fanpage pra ficar por dentro das novidades:
31 | https://www.linkedin.com/company/sharebook-br/ 32 |

33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Test.Unit/ShareBook.Test.Unit.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | all 17 | runtime; build; native; contentfiles; analyzers; buildtransitive 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Domain/AccessHistory.cs: -------------------------------------------------------------------------------- 1 | using ShareBook.Domain.Common; 2 | using ShareBook.Domain.Enums; 3 | 4 | using System; 5 | using System.ComponentModel.DataAnnotations.Schema; 6 | 7 | namespace ShareBook.Domain 8 | { 9 | public class AccessHistory : BaseEntity 10 | { 11 | protected AccessHistory() { } 12 | 13 | public AccessHistory(string visitorName, VisitorProfile profile) { 14 | ChangeVisitorName(visitorName); 15 | ChangeProfile(profile); 16 | } 17 | 18 | public AccessHistory(Guid id, string visitorName, VisitorProfile profile) : this(visitorName, profile) { 19 | UserId = id; 20 | } 21 | 22 | public Guid? UserId { get; private set; } 23 | [ForeignKey("UserId")] 24 | public User User { get; private set; } //Visitante do perfil 25 | public string VisitorName { get; private set; } 26 | public VisitorProfile Profile { get; private set; } 27 | 28 | public void ChangeVisitorName(string name) 29 | { 30 | if (string.IsNullOrEmpty(name)) return; 31 | 32 | VisitorName = name; 33 | } 34 | 35 | public void ChangeProfile(VisitorProfile newProfile) 36 | { 37 | if (Profile.Equals(newProfile)) return; 38 | 39 | Profile = newProfile; 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /ShareBook/ShareBook.Test.Integration/Tests/BookTests/BookTests.cs: -------------------------------------------------------------------------------- 1 | using System.Net; 2 | using Newtonsoft.Json; 3 | using ShareBook.Api.ViewModels; 4 | 5 | namespace ShareBook.Test.Integration.Tests.BookTests; 6 | 7 | [Collection(nameof(ShareBookTestsFixture))] 8 | public class BookTests 9 | { 10 | private readonly ShareBookTestsFixture _fixture; 11 | 12 | public BookTests(ShareBookTestsFixture fixture) 13 | { 14 | _fixture = fixture; 15 | } 16 | 17 | [Fact] 18 | public async Task AvailableBooks_All() 19 | { 20 | var response = await _fixture.ShareBookApiClient.GetAsync("api/book/AvailableBooks"); 21 | 22 | response.Should().NotBeNull(); 23 | response.StatusCode.Should().Be(HttpStatusCode.OK); 24 | string responseAsString = await response.Content.ReadAsStringAsync(); 25 | responseAsString.Should().NotBeNullOrWhiteSpace(); 26 | IList? books = JsonConvert.DeserializeObject>(responseAsString); 27 | books.Should().NotBeNullOrEmpty(); 28 | books!.Count.Should().Be(22); 29 | 30 | books!.All(i => 31 | !string.IsNullOrWhiteSpace(i.Title) 32 | && !string.IsNullOrWhiteSpace(i.Author) 33 | && !string.IsNullOrWhiteSpace(i.Slug) 34 | && i.CategoryId != default 35 | ).Should().BeTrue(); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /CLAUDE.md: -------------------------------------------------------------------------------- 1 | # CLAUDE.md - Memória de Longo Prazo 2 | 3 | ## 📋 Informações do Projeto 'Sharebook Api' 4 | 5 | Sharebook é nosso app livre e gratuito para doação de livros. Nosso backend é feito em .NET 8, com arquitetura limpa e testes unitários. O frontend é em Angular. 6 | 7 | ### Sobre o Desenvolvedor Raffa 8 | - Clean Code + Clean Architecture: modular, coeso, com separação clara de responsabilidades. 9 | - Valoriza boa organização do projeto, com bons nomes de pastas e arquivos. Vale a pena investir tempo nisso. 10 | - Valoriza nomes significativos e expressivos para componentes, hooks e funções. Vale a pena investir tempo nisso. 11 | - Odeia retrabalho — antes de criar, sempre verifica se já não existe pronto e gratuito. 12 | - Preza por segurança — validação e autorização bem feitas não são opcionais. 13 | - Gosta de impressionar — seja o cliente, o time ou a diretoria, sempre com um toque extra. 14 | - Não gosta de bajulação. Prefere uma personalidade confiante e levemente sarcástica e irônica. 15 | - Caso a tarefa não seja trivial, explique o seu plano antes de colocar a mão na massa. 16 | 17 | ### Dicas de ouro 18 | - Leve em consideração que o claude está rodando no powershell 19 | - Quando o usuário falar pra olhar a colinha, analise o arquivo "colinha.txt" na raíz. 20 | - Quando o usuário falar pra olhar o print 142, olhe o arquivo "C:\Users\brnra019\Documents\Lightshot\Screenshot_142.png" 21 | 22 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Api/Filters/AuthorizationFilter.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc.Filters; 2 | using ShareBook.Domain.Exceptions; 3 | using ShareBook.Service.Authorization; 4 | using System.Linq; 5 | using System.Security.Claims; 6 | 7 | namespace ShareBook.Api.Filters 8 | { 9 | public class AuthorizationFilter : ActionFilterAttribute 10 | { 11 | public Permissions.Permission[] NecessaryPermissions { get; set; } 12 | 13 | public AuthorizationFilter(params Permissions.Permission[] permissions) 14 | { 15 | NecessaryPermissions = permissions; 16 | } 17 | 18 | public override void OnActionExecuting(ActionExecutingContext context) 19 | { 20 | var user = context.HttpContext.User; 21 | 22 | if (user == null) 23 | throw new ShareBookException(ShareBookException.Error.NotAuthorized); 24 | 25 | var isAdministrator = ((ClaimsIdentity)user.Identity).Claims 26 | .Any(x => x.Type == ClaimsIdentity.DefaultRoleClaimType.ToString() && x.Value == Domain.Enums.Profile.Administrator.ToString()); 27 | 28 | if (NecessaryPermissions.Any(x => Permissions.AdminPermissions.Contains(x)) && !isAdministrator) 29 | throw new ShareBookException(ShareBookException.Error.Forbidden); 30 | 31 | base.OnActionExecuting(context); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Api/Controllers/Generic/BaseDeleteController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Authorization; 2 | using Microsoft.AspNetCore.Cors; 3 | using Microsoft.AspNetCore.Mvc; 4 | using ShareBook.Api.Filters; 5 | using ShareBook.Api.ViewModels; 6 | using ShareBook.Domain.Common; 7 | using ShareBook.Service.Generic; 8 | using System; 9 | using System.Threading.Tasks; 10 | 11 | namespace ShareBook.Api.Controllers 12 | { 13 | public class BaseDeleteController : BaseDeleteController 14 | where T : BaseEntity 15 | { 16 | public BaseDeleteController(IBaseService service) : base(service) { } 17 | } 18 | 19 | public class BaseDeleteController : BaseDeleteController 20 | where T : BaseEntity 21 | where R : BaseViewModel 22 | { 23 | public BaseDeleteController(IBaseService service) : base(service) { } 24 | } 25 | 26 | [GetClaimsFilter] 27 | [EnableCors("AllowAllHeaders")] 28 | public class BaseDeleteController : BaseController 29 | where T : BaseEntity 30 | where R : IIdProperty 31 | where A : class 32 | { 33 | 34 | public BaseDeleteController(IBaseService service) : base(service) { } 35 | 36 | [Authorize("Bearer")] 37 | [HttpDelete("{id}")] 38 | public async Task Delete(Guid id) => await _service.DeleteAsync(id); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Helper/DateTime/DateTimeHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace ShareBook.Helper 5 | { 6 | static public class DateTimeHelper 7 | { 8 | static private readonly string SaoPauloTimezoneId = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) 9 | ? "E. South America Standard Time" 10 | : "America/Sao_Paulo"; 11 | 12 | // hora agora. 13 | static public TimeSpan GetTimeNowSaoPaulo() 14 | { 15 | var now = TimeZoneInfo.ConvertTime(DateTime.Now, TimeZoneInfo.FindSystemTimeZoneById(SaoPauloTimezoneId)); 16 | var today = new DateTime(now.Year, now.Month, now.Day); 17 | return now - today; 18 | } 19 | 20 | // data hora agora. 21 | static public DateTime GetDateTimeNowSaoPaulo() => TimeZoneInfo.ConvertTime(DateTime.Now, TimeZoneInfo.FindSystemTimeZoneById(SaoPauloTimezoneId)); 22 | 23 | // data-hora de hoje a meia noite. 24 | static public DateTime GetTodaySaoPaulo() 25 | { 26 | var nowSP = GetDateTimeNowSaoPaulo(); 27 | var todaySP = new DateTime(nowSP.Year, nowSP.Month, nowSP.Day, 0, 0, 0); 28 | return todaySP; 29 | } 30 | 31 | static public DateTime ConvertDateTimeSaoPaulo(DateTime d) => TimeZoneInfo.ConvertTime(d, TimeZoneInfo.FindSystemTimeZoneById(SaoPauloTimezoneId)); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Test.Integration/Tests/CategoryTests/CategoryTests.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using ShareBook.Domain; 3 | using ShareBook.Domain.Common; 4 | using System.Net; 5 | 6 | namespace ShareBook.Test.Integration.Tests.CategoryTests; 7 | 8 | [Collection(nameof(ShareBookTestsFixture))] 9 | public class CategoryTests 10 | { 11 | private readonly ShareBookTestsFixture _fixture; 12 | 13 | public CategoryTests(ShareBookTestsFixture fixture) 14 | { 15 | _fixture = fixture; 16 | } 17 | 18 | [Fact] 19 | public async Task GetCategories() 20 | { 21 | var response = await _fixture.ShareBookApiClient.GetAsync("api/category"); 22 | 23 | response.Should().NotBeNull(); 24 | response.StatusCode.Should().Be(HttpStatusCode.OK); 25 | string responseAsString = await response.Content.ReadAsStringAsync(); 26 | responseAsString.Should().NotBeNullOrWhiteSpace(); 27 | PagedList? categories = JsonConvert.DeserializeObject>(responseAsString); 28 | categories.Should().NotBeNull(); 29 | categories!.Items.Should().NotBeNull(); 30 | categories.Items.Count.Should().Be(10); 31 | categories.ItemsPerPage.Should().Be(50); 32 | categories.Page.Should().Be(1); 33 | 34 | categories.Items.All(i => 35 | !string.IsNullOrWhiteSpace(i.Name) 36 | && i.Id != default 37 | ).Should().BeTrue(); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Test.Integration/Setup/ShareBookWebAppFactory.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Hosting; 2 | using Microsoft.AspNetCore.Mvc.Testing; 3 | using Microsoft.AspNetCore.TestHost; 4 | using Microsoft.EntityFrameworkCore; 5 | using Microsoft.Extensions.DependencyInjection; 6 | using Microsoft.Extensions.Hosting; 7 | using ShareBook.Api; 8 | using ShareBook.Repository; 9 | 10 | namespace ShareBook.Test.Integration.Setup; 11 | 12 | public class ShareBookWebAppFactory : WebApplicationFactory 13 | { 14 | protected override IHostBuilder? CreateHostBuilder() 15 | { 16 | return Host.CreateDefaultBuilder() 17 | .ConfigureWebHostDefaults(webBuilder => 18 | { 19 | webBuilder.UseStartup(); 20 | }); 21 | } 22 | 23 | protected override void ConfigureWebHost(IWebHostBuilder builder) 24 | { 25 | base.ConfigureWebHost(builder); 26 | Startup.IgnoreMigrations = true; 27 | 28 | builder.ConfigureTestServices(services => 29 | { 30 | var dbOptions = services.FirstOrDefault(x => x.ServiceType == typeof(DbContextOptions)); 31 | if (dbOptions != null) 32 | services.Remove(dbOptions); 33 | 34 | services.AddDbContext(options => 35 | { 36 | options.UseInMemoryDatabase("ShareBookInMemoryDb"); 37 | }); 38 | }); 39 | } 40 | } -------------------------------------------------------------------------------- /ShareBook/ShareBook.Repository/Repository/Meetup/MeetupRepository.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using ShareBook.Domain; 3 | using ShareBook.Domain.Common; 4 | using ShareBook.Repository.Repository; 5 | using System; 6 | using System.Linq; 7 | using System.Linq.Expressions; 8 | using System.Threading.Tasks; 9 | 10 | namespace ShareBook.Repository 11 | { 12 | public class MeetupRepository : RepositoryGeneric, IMeetupRepository 13 | { 14 | public MeetupRepository(ApplicationDbContext context) : base(context) 15 | { 16 | 17 | } 18 | 19 | public override async Task> GetAsync(Expression> filter, Expression> order, int page, int itemsPerPage, IncludeList includes) 20 | { 21 | var skip = (page - 1) * itemsPerPage; 22 | var query = _dbSet.AsQueryable(); 23 | 24 | query = query.Where(filter); 25 | var total = await query.CountAsync(); 26 | var result = await query 27 | .OrderByDescending(order) 28 | .Skip(skip) 29 | .Take(itemsPerPage) 30 | .ToListAsync(); 31 | 32 | return new PagedList() 33 | { 34 | Page = page, 35 | ItemsPerPage = itemsPerPage, 36 | TotalItems = total, 37 | Items = result 38 | }; 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /ShareBook/ShareBook.Service/ContactUs/ContactUsService.cs: -------------------------------------------------------------------------------- 1 | using FluentValidation; 2 | using ShareBook.Domain; 3 | using ShareBook.Domain.Common; 4 | using ShareBook.Service.Recaptcha; 5 | using System.Threading.Tasks; 6 | 7 | namespace ShareBook.Service 8 | { 9 | public class ContactUsService : IContactUsService 10 | { 11 | readonly IContactUsEmailService _contactUsEmailService; 12 | readonly IValidator _validator; 13 | readonly IRecaptchaService _recaptchaService; 14 | public ContactUsService(IContactUsEmailService contactUsEmailService, IValidator validator, IRecaptchaService recaptchaService) 15 | { 16 | _contactUsEmailService = contactUsEmailService; 17 | _validator = validator; 18 | _recaptchaService = recaptchaService; 19 | } 20 | public async Task> SendContactUsAsync(ContactUs contactUs, string recaptchaReactive) 21 | { 22 | 23 | var result = new Result(_validator.Validate(contactUs)); 24 | 25 | Result resultRecaptcha = _recaptchaService.SimpleValidationRecaptcha(recaptchaReactive); 26 | if (!resultRecaptcha.Success) 27 | result.Messages.AddRange(resultRecaptcha.Messages); 28 | 29 | if (!result.Success) 30 | return result; 31 | 32 | await _contactUsEmailService.SendEmailContactUsAsync(contactUs); 33 | 34 | return result; 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Repository/ApplicationDbContextFactory.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using Microsoft.EntityFrameworkCore.Design; 3 | using Microsoft.Extensions.Configuration; 4 | using System.IO; 5 | 6 | namespace ShareBook.Repository; 7 | 8 | public class ApplicationDbContextFactory : IDesignTimeDbContextFactory 9 | { 10 | public ApplicationDbContext CreateDbContext(string[] args) 11 | { 12 | var configuration = new ConfigurationBuilder() 13 | .SetBasePath(Directory.GetCurrentDirectory()) 14 | .AddJsonFile("appsettings.json") 15 | .AddJsonFile("appsettings.Development.json", optional: true) 16 | .Build(); 17 | 18 | var optionsBuilder = new DbContextOptionsBuilder(); 19 | var dbProvider = configuration["DatabaseProvider"]?.ToLower() ?? "sqlserver"; 20 | 21 | switch (dbProvider) 22 | { 23 | case "postgres": 24 | optionsBuilder.UseNpgsql(configuration.GetConnectionString("PostgresConnection")); 25 | break; 26 | 27 | case "sqlite": 28 | optionsBuilder.UseSqlite(configuration.GetConnectionString("SqliteConnection")); 29 | break; 30 | 31 | default: 32 | optionsBuilder.UseSqlServer(configuration.GetConnectionString("DefaultConnection")); 33 | break; 34 | } 35 | 36 | return new ApplicationDbContext(optionsBuilder.Options); 37 | } 38 | } -------------------------------------------------------------------------------- /ShareBook/ShareBook.Test.Unit/Validators/ContactUsValidatorTests.cs: -------------------------------------------------------------------------------- 1 | using FluentValidation.Results; 2 | using ShareBook.Domain; 3 | using ShareBook.Domain.Validators; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | using Xunit; 10 | 11 | namespace ShareBook.Test.Unit.Validators 12 | { 13 | public class ContactUsValidatorTests 14 | { 15 | ContactUsValidator contactUsValidator = new ContactUsValidator(); 16 | 17 | [Fact] 18 | public void ValidEntities() 19 | { 20 | ContactUs contactUs = new ContactUs 21 | { 22 | Email = "joao@sharebook.com.br", 23 | Message = "Essa mensagem é obrigatória", 24 | Name = "Joao", 25 | Phone = "4499988-7766", 26 | }; 27 | ValidationResult result = contactUsValidator.Validate(contactUs); 28 | Assert.True(result.IsValid); 29 | } 30 | 31 | [Fact] 32 | public void InvalidEmail() 33 | { 34 | ContactUs contactUs = new ContactUs 35 | { 36 | Email = "joaoarebook.com.br", 37 | Message = "Essa mensagem é obrigatória", 38 | Name = "Joao", 39 | Phone = "4499988-7766", 40 | }; 41 | 42 | ValidationResult result = contactUsValidator.Validate(contactUs); 43 | Assert.False(result.IsValid); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Service/Email/Templates/LateDonationNotification.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SHAREBOOK - STATUS DO DIA. 6 | 7 | 8 |

9 | Bom dia! Segue o status de hoje: 10 |

11 | 12 |
    13 |
  • Aguardando aprovação: {totalWaitingApproval}
  • 14 |
  • Em atraso: {totalLate}
  • 15 |
  • Concluídas: {totalOk}
  • 16 |
17 | 18 |

Cada doação concluída, faz uma pessoa feliz. Bora Pra cima!

19 | 20 | {htmlTable} 21 | 22 |

Por favor, faça contato com os doadores os ajude a concluir sua doação. Isso é muito importante para que nossos usuários tenham uma boa experiência com nossa plataforma. Afinal ninguém gosta de atrasos, certo?

23 | 24 |

 

25 | 26 |

27 | Atenciosamente,

28 | 29 | Sharebook team - Compartilhando conhecimento
30 | Siga-nos no instagram: 31 | @sharebook.com.br 32 |
33 |
34 | Curta nossa fanpage pra ficar por dentro das novidades:
35 | https://www.linkedin.com/company/sharebook-br/ 36 |

37 | 38 | 39 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Service/Email/Templates/BookNoticeDeclinedUsersTemplate.html: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | Resultado da doação do livro {BookTitle} 7 | 8 | 9 | 10 | 11 |

12 | Olá,

13 | 14 | Aqui é o Sharebot do time Sharebook. Blz?

15 | 16 | Obrigado por ter demonstrado interesse no livro "{BookTitle}". Mas infelizmente o doador escolheu outro(a) ganhador(a). =(

17 | 18 | Sabemos que essa não era a notícia que você gostaria de receber. O doador leu com carinho todos os pedidos e escolheu aquele que parecia mais precisar e mais interessado.

19 | 20 | Por favor não desanime. Toda semana entram muitos livros novos e certamente um dia vc vai ganhar o seu. Ou até mesmo esse livro pode ser doado de novo. Blz? 21 |
22 |

23 | 24 |

25 | Atenciosamente,

26 | 27 | Sharebook team - Compartilhando conhecimento
28 | Siga-nos no instagram: 29 | @sharebook.com.br 30 |
31 |
32 | Curta nossa fanpage pra ficar por dentro das novidades:
33 | https://www.linkedin.com/company/sharebook-br/ 34 |

35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Domain/MailBounce.cs: -------------------------------------------------------------------------------- 1 | using ShareBook.Domain.Common; 2 | using System.Text.RegularExpressions; 3 | 4 | namespace ShareBook.Domain; 5 | 6 | public class MailBounce: BaseEntity 7 | { 8 | public string? Email { get; set; } 9 | public string? Subject { get; set; } 10 | public string? Body { get; set; } 11 | public string? ErrorCode { get; set; } 12 | public bool IsSoft { get; set; } = false; 13 | public bool IsBounce { get; set; } = false; 14 | 15 | public MailBounce(string subject, string body) 16 | { 17 | Subject = subject; 18 | Body = body; 19 | 20 | ExtractFromBody(); 21 | } 22 | 23 | private void ExtractFromBody() 24 | { 25 | if (string.IsNullOrEmpty(Body)) return; 26 | 27 | // tenta extrair o email de destino original do corpo do email 28 | string pattern = @"\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*"; 29 | Match match = Regex.Match(Body, pattern); 30 | Email = match.Success ? match.Value : ""; 31 | 32 | // Check if email body contains an error code 33 | var errorCodeMatch = Regex.Match(Body, @"Remote Server returned: '(\d{3})"); 34 | 35 | if (errorCodeMatch.Success) 36 | { 37 | IsBounce = true; 38 | ErrorCode = errorCodeMatch.Groups.Count == 2 ? errorCodeMatch.Groups[1].Value : ""; 39 | 40 | if (ErrorCode.StartsWith("4")) 41 | { 42 | // Soft bounce 43 | IsSoft = true; 44 | } 45 | } 46 | } 47 | 48 | } -------------------------------------------------------------------------------- /ShareBook/Sharebook.Jobs/Jobs/4 - MeetupSearch.cs: -------------------------------------------------------------------------------- 1 | using ShareBook.Repository; 2 | using ShareBook.Service; 3 | using ShareBook.Domain.Enums; 4 | using System; 5 | using ShareBook.Domain; 6 | using ShareBook.Domain.Exceptions; 7 | using Microsoft.Extensions.Configuration; 8 | using System.Threading.Tasks; 9 | 10 | namespace Sharebook.Jobs; 11 | 12 | public class MeetupSearch : GenericJob, IJob 13 | { 14 | private readonly IMeetupService _meetupService; 15 | private readonly IConfiguration _configuration; 16 | public MeetupSearch(IJobHistoryRepository jobHistoryRepo, IMeetupService meetupService, IConfiguration configuration) : base(jobHistoryRepo) 17 | { 18 | _meetupService = meetupService; 19 | 20 | JobName = "MeetupSearch"; 21 | Description = "Atualiza a lista de Meetups do Sharebook com base no youtube."; 22 | Interval = Interval.Dayly; 23 | Active = true; 24 | BestTimeToExecute = new TimeSpan(1, 0, 0); 25 | _configuration = configuration; 26 | } 27 | 28 | public override async Task WorkAsync() 29 | { 30 | var meetupEnabled = bool.Parse(_configuration["MeetupSettings:IsActive"]); 31 | if(!meetupEnabled) throw new MeetupDisabledException("Serviço Meetup está desabilitado no appsettings."); 32 | 33 | var jobResult = await _meetupService.FetchMeetupsAsync(); 34 | 35 | return new JobHistory() 36 | { 37 | JobName = JobName, 38 | IsSuccess = true, 39 | Details = string.Join("\n", jobResult) 40 | }; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Service/Meetup/Dto/YoutubeDTO.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace ShareBook.Service.Dto; 5 | 6 | public class YoutubeDto 7 | { 8 | public string nextPageToken { get; set; } 9 | public string prevPageToken { get; set; } 10 | public PageInfo pageInfo { get; set; } 11 | public List Items { get; set; } 12 | } 13 | public class PageInfo 14 | { 15 | public int TotalResults { get; set; } 16 | public int ResultsPerPage { get; set; } 17 | } 18 | 19 | public class Item 20 | { 21 | public Id Id { get; set; } 22 | public Snippet Snippet { get; set; } 23 | } 24 | 25 | public class Id 26 | { 27 | public string Kind { get; set; } 28 | public string VideoId { get; set; } 29 | } 30 | 31 | public class Snippet 32 | { 33 | public DateTime PublishedAt { get; set; } 34 | public string ChannelId { get; set; } 35 | public string Title { get; set; } 36 | public string Description { get; set; } 37 | public ThumbnailsDto Thumbnails { get; set; } 38 | public string ChannelTitle { get; set; } 39 | public string LiveBroadcastContent { get; set; } 40 | public DateTime PublishTime { get; set; } 41 | } 42 | 43 | public class ThumbnailsDto 44 | { 45 | public ThumbnailDetailDto Default { get; set; } 46 | public ThumbnailDetailDto Medium { get; set; } 47 | public ThumbnailDetailDto High { get; set; } 48 | } 49 | 50 | public class ThumbnailDetailDto 51 | { 52 | public string Url { get; set; } 53 | public int Width { get; set; } 54 | public int Height { get; set; } 55 | } 56 | 57 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Repository/UtcContext.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using Microsoft.EntityFrameworkCore.Storage.ValueConversion; 3 | using System; 4 | 5 | namespace ShareBook.Repository 6 | { 7 | public static class UtcContext 8 | { 9 | public static void SetUtcOnDatabase(this ApplicationDbContext context, ModelBuilder builder) 10 | { 11 | var dateTimeConverter = new ValueConverter( 12 | v => v.ToUniversalTime(), 13 | v => DateTime.SpecifyKind(v, DateTimeKind.Utc)); 14 | 15 | var nullableDateTimeConverter = new ValueConverter( 16 | v => v.HasValue ? v.Value.ToUniversalTime() : v, 17 | v => v.HasValue ? DateTime.SpecifyKind(v.Value, DateTimeKind.Utc) : v); 18 | 19 | foreach (var entityType in builder.Model.GetEntityTypes()) 20 | { 21 | if (entityType.IsKeyless) 22 | { 23 | continue; 24 | } 25 | 26 | foreach (var property in entityType.GetProperties()) 27 | { 28 | if (property.ClrType == typeof(DateTime)) 29 | { 30 | property.SetValueConverter(dateTimeConverter); 31 | } 32 | else if (property.ClrType == typeof(DateTime?)) 33 | { 34 | property.SetValueConverter(nullableDateTimeConverter); 35 | } 36 | } 37 | } 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /ShareBook/ShareBook.Api/Controllers/MeetupController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Cors; 2 | using Microsoft.AspNetCore.Mvc; 3 | using ShareBook.Domain; 4 | using ShareBook.Domain.Common; 5 | using ShareBook.Service; 6 | using System; 7 | using System.Collections.Generic; 8 | using System.Threading.Tasks; 9 | 10 | namespace ShareBook.Api.Controllers 11 | { 12 | [Route("api/[controller]")] 13 | [EnableCors("AllowAllHeaders")] 14 | public class MeetupController : ControllerBase 15 | { 16 | private readonly IMeetupService _meetupService; 17 | public MeetupController(IMeetupService meetupService) 18 | { 19 | _meetupService = meetupService; 20 | } 21 | [HttpGet] 22 | 23 | public async Task> GetAsync(int? page, int? pageSize, bool upcoming = false) 24 | { 25 | return await _meetupService.GetAsync(upcoming ? x => x.Active && x.StartDate > DateTime.Now : x => x.Active && x.StartDate <= DateTime.Now, x => x.StartDate, page ?? 1, pageSize ?? 10); 26 | } 27 | 28 | [HttpGet("{id}")] 29 | public async Task GetAsync(string id) 30 | { 31 | if (!Guid.TryParse(id, out var meetupId)) 32 | BadRequest(); 33 | 34 | var meetup = await _meetupService.FindAsync(x => x.Id == meetupId); 35 | return meetup != null ? Ok(meetup) : NotFound(); 36 | } 37 | 38 | [HttpGet("Search")] 39 | public async Task> SearchAsync([FromQuery]string criteria) 40 | { 41 | return await _meetupService.SearchAsync(criteria); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Api/Filters/ThrottleFilter.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using Microsoft.AspNetCore.Mvc.Filters; 3 | using Microsoft.Extensions.Caching.Memory; 4 | using System; 5 | using System.Net; 6 | 7 | namespace ShareBook.Api.Filters 8 | { 9 | [AttributeUsage(AttributeTargets.Method)] 10 | public class ThrottleAttribute : ActionFilterAttribute 11 | { 12 | public string Name { get; set; } 13 | public int Seconds { get; set; } 14 | public string Message { get; set; } 15 | public bool VaryByIp { get; set; } 16 | 17 | private static MemoryCache Cache { get; } = new MemoryCache(new MemoryCacheOptions()); 18 | 19 | public override void OnActionExecuting(ActionExecutingContext c) 20 | { 21 | var key = VaryByIp 22 | ? string.Concat(Name, "-", c.HttpContext.Request.HttpContext.Connection.RemoteIpAddress) 23 | : Name; 24 | 25 | if (!Cache.TryGetValue(key, out bool entry)) 26 | { 27 | var cacheEntryOptions = new MemoryCacheEntryOptions() 28 | .SetAbsoluteExpiration(TimeSpan.FromSeconds(Seconds)); 29 | 30 | Cache.Set(key, true, cacheEntryOptions); 31 | } 32 | else 33 | { 34 | if (string.IsNullOrEmpty(Message)) 35 | Message = "You may only perform this action every {n} seconds."; 36 | 37 | c.Result = new ContentResult { Content = Message.Replace("{n}", Seconds.ToString()) }; 38 | c.HttpContext.Response.StatusCode = (int)HttpStatusCode.Conflict; 39 | } 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Helper/ClientVersionValidation/ClientVersionValidation.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text.RegularExpressions; 3 | 4 | namespace ShareBook.Helper 5 | { 6 | static public class ClientVersionValidation 7 | { 8 | static public bool IsValidVersion(string version, string minVersion) 9 | { 10 | try 11 | { 12 | (int majorMin, int minorMin, int patchMin) = VersionDeconstructor(minVersion); 13 | (int major, int minor, int patch) = VersionDeconstructor(version); 14 | 15 | if (major < majorMin) return false; 16 | if (major > majorMin) return true; 17 | 18 | if (minor < minorMin) return false; 19 | if (minor > minorMin) return true; 20 | 21 | if (patch < patchMin) return false; 22 | else return true; 23 | } 24 | catch (Exception) 25 | { 26 | return false; 27 | } 28 | 29 | } 30 | 31 | static private Tuple VersionDeconstructor(string version) 32 | { 33 | string pattern = @"v([0-9]{1,2})\.([0-9]{1,2})\.([0-9]{1,2})$"; 34 | Regex rg = new Regex(pattern); 35 | 36 | MatchCollection matches = rg.Matches(version); 37 | 38 | if (matches.Count != 1) throw new Exception("Formato inválido"); 39 | 40 | var major = int.Parse(matches[0].Groups[1].Value); 41 | var minor = int.Parse(matches[0].Groups[2].Value); 42 | var patch = int.Parse(matches[0].Groups[3].Value); 43 | 44 | return Tuple.Create(major, minor, patch); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Repository/Mapping/BookMap.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using Microsoft.EntityFrameworkCore.Metadata.Builders; 3 | using ShareBook.Domain; 4 | 5 | namespace ShareBook.Repository.Mapping 6 | { 7 | public class BookMap : IEntityTypeConfiguration 8 | { 9 | public void Configure(EntityTypeBuilder entityBuilder) 10 | { 11 | entityBuilder.HasKey(t => t.Id); 12 | 13 | entityBuilder.Property(t => t.UserId); 14 | 15 | entityBuilder.Property(t => t.UserIdFacilitator); 16 | 17 | entityBuilder.Property(t => t.Author) 18 | .HasMaxLength(200) 19 | .IsRequired(); 20 | 21 | entityBuilder.Property(t => t.Title) 22 | .HasMaxLength(200) 23 | .IsRequired(); 24 | 25 | entityBuilder.Property(t => t.ImageSlug) 26 | .HasMaxLength(100) 27 | .IsRequired(); 28 | 29 | entityBuilder.Property(t => t.Slug) 30 | .HasMaxLength(100); 31 | 32 | entityBuilder.Property(t => t.Synopsis) 33 | .HasMaxLength(2000); 34 | 35 | entityBuilder.Property(t => t.FacilitatorNotes) 36 | .HasMaxLength(2000); 37 | 38 | entityBuilder.Ignore(t => t.ImageBytes); 39 | 40 | entityBuilder.Ignore(t => t.ImageUrl); 41 | 42 | entityBuilder.Ignore(t => t.ImageName); 43 | 44 | entityBuilder.Ignore(t => t.EBookPdfBytes); 45 | 46 | entityBuilder.HasOne(t => t.User); 47 | 48 | entityBuilder.HasOne(t => t.UserFacilitator); 49 | 50 | entityBuilder.HasOne(t => t.Category); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Service/Email/Templates/BookTrackingNumberNoticeWinnerTemplate.html: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | SHAREBOOK - Livro postado 7 | 8 | 9 | 10 | 11 |

12 | Olá,

13 | 14 | Temos uma notícia boa. 15 |
O livro {book.Title} foi postado e você pode acompanhar o rastreio no site 16 | dos Correios com o código 17 | {book.TrackingNumber} . 18 | 19 |

20 | 21 |

22 | Em caso de dúvidas ou atraso não hesite em falar com o Facilitador. Nós queremos muito que o livro 23 | chegue até você o mais rápido possível. 24 |

25 | 26 |
    27 |
  • Nome do Facilitador: {NameFacilitator}
  • 28 |
  • LinkedIn: {LinkedInFacilitator}
  • 29 |
  • Whatsapp: {ZapFacilitator}
  • 30 |
  • Email: {EmailFacilitator}
  • 31 |
32 | 33 |

34 | Atenciosamente,

35 | 36 | Sharebook team - Compartilhando conhecimento
37 | Siga-nos no instagram: 38 | @sharebook.com.br 39 |
40 |
41 | Curta nossa fanpage pra ficar por dentro das novidades:
42 | https://www.linkedin.com/company/sharebook-br/ 43 |

44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Service/Email/Templates/NewBookInsertedTemplate.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Novo livro cadastrado - Sharebook 6 | 7 | 8 |

9 | Olá Administrador(a), 10 |

11 |

12 | Um novo livro foi cadastrado. Veja mais informações abaixo: 13 |

14 | 15 |
    16 |
  • Livro: {Book.Title}
  • 17 |
  • Autor: {Book.Author}
  • 18 |
  • Usuário: {Book.User.Name}
  • 19 |
20 | 21 |

22 | Informações sobre o usuário: 23 |

24 | 25 |
    26 |
  • Data de Cadastro: {UserStats.CreationDate}
  • 27 |
  • Total doações concluídas: {UserStats.TotalOk}
  • 28 |
  • Total doações canceladas: {UserStats.TotalCanceled}
  • 29 |
  • Total doações em atraso: {UserStats.TotalLate}
  • 30 |
  • Total doações em aguardando aprovação: {UserStats.TotalWaitingApproval}
  • 31 |
  • Total doações na vitrine: {UserStats.TotalAvailable}
  • 32 |
33 | 34 |

35 | Use esse link para revisar e aprovar: https://www.sharebook.com.br/book/form/{Book.Id} 36 |

37 | 38 |

39 | Cuidado. Geralmente não sabemos se um NOVO USUÁRIO é firmeza. O recomendado é aprovar apenas um livro de início. Caso ele realmente conclua a doação, aí temos segurança para aprovar todos. Blz? 40 |

41 | 42 | 43 |

Sharebook

44 | 45 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Service/Book/IBookService.cs: -------------------------------------------------------------------------------- 1 | using ShareBook.Domain; 2 | using ShareBook.Domain.Common; 3 | using ShareBook.Domain.DTOs; 4 | using ShareBook.Domain.Enums; 5 | using ShareBook.Service.Generic; 6 | using System; 7 | using System.Collections.Generic; 8 | using System.Threading.Tasks; 9 | 10 | namespace ShareBook.Service 11 | { 12 | public interface IBookService : IBaseService 13 | { 14 | Task ApproveAsync(Guid bookId, DateTime? chooseDate); 15 | 16 | Task ReceivedAsync(Guid bookId, Guid winnerUserId); 17 | Task UpdateBookStatusAsync(Guid bookId, BookStatus bookStatus); 18 | 19 | IList FreightOptions(); 20 | 21 | Task> AvailableBooksAsync(); 22 | 23 | Task> Random15BooksAsync(); 24 | 25 | Task> Random15EBooksAsync(); 26 | 27 | Task> FullSearchAsync(string criteria, int page, int itemsPerPage, bool isAdmin = false); 28 | 29 | Task> ByCategoryIdAsync(Guid categoryId, int page, int items); 30 | 31 | Task> GetAllAsync(int page, int items); 32 | 33 | Task BySlugAsync(string slug); 34 | 35 | Task UserRequestedBookAsync(Guid bookId); 36 | 37 | Task> GetUserDonationsAsync(Guid userId); 38 | 39 | Task> GetBooksChooseDateIsTodayAsync(); 40 | 41 | Task> GetBooksChooseDateIsLateAsync(); 42 | 43 | Task> GetBooksChooseDateIsTodayOrLateAsync(); 44 | 45 | Task AddFacilitatorNotesAsync(Guid bookId, string facilitatorNotes); 46 | 47 | Task GetBookWithAllUsersAsync(Guid bookId); 48 | 49 | Task RenewChooseDateAsync(Guid bookId); 50 | Task GetStatsAsync(); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Service/ContactUs/ContactUsEmailService.cs: -------------------------------------------------------------------------------- 1 | using ShareBook.Domain; 2 | using System.Threading.Tasks; 3 | 4 | namespace ShareBook.Service 5 | { 6 | public class ContactUsEmailService : IContactUsEmailService 7 | { 8 | private const string ContactUsTemplate = "ContactUsTemplate"; 9 | public const string ContactUsTitle = "Fale Conosco - Sharebook"; 10 | private const string ContactUsNotificationTemplate = "ContactUsNotificationTemplate"; 11 | public const string ContactUsNotificationTitle = "Fale Conosco - Sharebook"; 12 | 13 | private readonly IEmailService _emailService; 14 | 15 | private readonly IEmailTemplate _emailTemplate; 16 | 17 | 18 | public ContactUsEmailService(IEmailService emailService, IEmailTemplate emailTemplate) 19 | { 20 | _emailService = emailService; 21 | _emailTemplate = emailTemplate; 22 | } 23 | public async Task SendEmailContactUsAsync(ContactUs contactUs) 24 | { 25 | await SendEmailContactUsToAdministratorAsync(contactUs); 26 | 27 | await SendEmailNotificationToUserAsync(contactUs); 28 | } 29 | private async Task SendEmailContactUsToAdministratorAsync(ContactUs contactUs) 30 | { 31 | var html = await _emailTemplate.GenerateHtmlFromTemplateAsync(ContactUsTemplate, contactUs); 32 | await _emailService.SendToAdminsAsync(html, ContactUsTitle); 33 | } 34 | private async Task SendEmailNotificationToUserAsync(ContactUs contactUs) 35 | { 36 | var html = await _emailTemplate.GenerateHtmlFromTemplateAsync(ContactUsNotificationTemplate, contactUs); 37 | await _emailService.SendAsync(contactUs.Email, contactUs.Name, html, ContactUsNotificationTitle, copyAdmins: false, highPriority: true); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Repository/ApplicationDbContext.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using ShareBook.Domain; 3 | using ShareBook.Repository.Mapping; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | 7 | namespace ShareBook.Repository 8 | { 9 | public class ApplicationDbContext : DbContext 10 | { 11 | public ApplicationDbContext(DbContextOptions options) : base(options) { } 12 | public ApplicationDbContext() { } 13 | 14 | public DbSet Books { get; set; } 15 | public DbSet Users { get; set; } 16 | public DbSet LogEntries { get; set; } 17 | public DbSet BookUser { get; set; } 18 | public DbSet Categories { get; set; } 19 | public DbSet
Addresses { get; set; } 20 | public DbSet JobHistories { get; set; } 21 | public DbSet AccessHistories { get; set; } 22 | public DbSet Meetups { get; set; } 23 | public DbSet MeetupParticipants { get; set; } 24 | 25 | public DbSet MailBounces { get; set; } 26 | 27 | protected override void OnModelCreating(ModelBuilder modelBuilder) 28 | { 29 | base.OnModelCreating(modelBuilder); 30 | 31 | //O Contexto procura pelas classes que implementam IEntityTypeConfiguration adicionando o mapeamento de forma automática. 32 | modelBuilder.ApplyConfigurationsFromAssembly(typeof(ApplicationDbContext).Assembly); 33 | 34 | this.SetUtcOnDatabase(modelBuilder); 35 | } 36 | 37 | public override async Task SaveChangesAsync(CancellationToken cancellationToken = default(CancellationToken)) 38 | { 39 | await this.LogChanges(); 40 | return await base.SaveChangesAsync(cancellationToken); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Service/Email/Templates/ChooseDateReminderTemplate.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SHAREBOOK - É HOJE! 6 | 7 | 8 |

9 | Olá {DonorName}, 10 |

11 | 12 |

Aqui é o Sharebot do time Sharebook. Tudo bem?

13 | 14 |

Hoje é o grande dia. O dia de escolher o ganhador do livro '{BookTitle}'! Pra sua conveniência use o link abaixo:

15 | 16 |

https://sharebook.com.br/book/donations

17 | 18 |

19 | Por favor escolha o ganhador o quanto antes. É muito importante.
20 | Afinal todos os interessados estão anciosos pelo dia de hoje. Obrigado! 21 |

22 | 23 |

24 | Dúvidas? Sem problemas, é um prazer ajudar. Entre em contato com seu facilitador. 25 |

31 |

32 | 33 |

 

34 | 35 |

36 | Atenciosamente,

37 | 38 | Sharebook team - Compartilhando conhecimento
39 | Siga-nos no instagram: 40 | @sharebook.com.br 41 |
42 |
43 | Curta nossa fanpage pra ficar por dentro das novidades:
44 | https://www.linkedin.com/company/sharebook-br/ 45 |

46 | 47 | 48 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Repository/Repository/Book/BookRepository.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Linq.Expressions; 4 | using System.Threading.Tasks; 5 | using Microsoft.EntityFrameworkCore; 6 | using ShareBook.Domain; 7 | using ShareBook.Domain.Common; 8 | 9 | namespace ShareBook.Repository 10 | { 11 | public class BookRepository : RepositoryGeneric, IBookRepository 12 | { 13 | public BookRepository(ApplicationDbContext context) : base(context) { } 14 | 15 | public override async Task UpdateAsync(Book entity) 16 | { 17 | 18 | _context.Update(entity); 19 | 20 | //imagem eh opcional no update 21 | if (entity.ImageSlug == null) 22 | _context.Entry(entity).Property(x => x.ImageSlug).IsModified = false; 23 | 24 | if(entity.Slug == null) 25 | _context.Entry(entity).Property(x => x.Slug).IsModified = false; 26 | 27 | _context.Entry(entity).Property(x => x.UserId).IsModified = false; 28 | 29 | await _context.SaveChangesAsync(); 30 | 31 | return entity; 32 | } 33 | 34 | public override async Task> GetAsync(Expression> filter, Expression> order, int page, int itemsPerPage) 35 | { 36 | var skip = (page - 1) * itemsPerPage; 37 | var query = _dbSet.Where(filter); 38 | var total = await query.CountAsync(); 39 | var result = await query.Include(x => x.BookUsers).Include(x => x.User) 40 | .OrderBy(order) 41 | .Skip(skip) 42 | .Take(itemsPerPage) 43 | .ToListAsync(); 44 | 45 | return new PagedList() 46 | { 47 | Page = page, 48 | ItemsPerPage = itemsPerPage, 49 | TotalItems = total, 50 | Items = result 51 | }; 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Test.Unit/Validators/BookValidatorTests.cs: -------------------------------------------------------------------------------- 1 | using FluentValidation.Results; 2 | using ShareBook.Domain; 3 | using ShareBook.Domain.Enums; 4 | using ShareBook.Domain.Validators; 5 | using System; 6 | using System.Text; 7 | using Xunit; 8 | 9 | namespace ShareBook.Test.Unit.Validators 10 | { 11 | public class BookValidatorTests 12 | { 13 | BookValidator bookValidator = new BookValidator(); 14 | 15 | [Fact] 16 | public void ValidEntities() 17 | { 18 | Book book = new Book() 19 | { 20 | Title = "Lord of the Rings", 21 | Author = "J. R. R. Tolkien", 22 | ImageName = "lotr.png", 23 | ImageBytes = Encoding.UTF8.GetBytes("STRINGBASE64"), 24 | FreightOption = FreightOption.World, 25 | UserId = new Guid("5489A967-9320-4350-E6FC-08D5CC8498F3"), 26 | CategoryId = Guid.NewGuid() 27 | }; 28 | 29 | ValidationResult result = bookValidator.Validate(book); 30 | 31 | Assert.True(result.IsValid); 32 | } 33 | 34 | 35 | [Fact] 36 | public void InvalidEntities() 37 | { 38 | Book book = new Book() 39 | { 40 | Title = "Lord of the Rings", 41 | Author = null, 42 | ImageName = "lotr.png" 43 | }; 44 | 45 | ValidationResult result = bookValidator.Validate(book); 46 | 47 | Assert.False(result.IsValid); 48 | } 49 | 50 | 51 | [Fact] 52 | public void InvalidImageExtension() 53 | { 54 | Book book = new Book() 55 | { 56 | Title = "Lord of the Rings", 57 | Author = "J. R. R. Tolkien", 58 | ImageName = "lotrnoextension" 59 | }; 60 | 61 | ValidationResult result = bookValidator.Validate(book); 62 | 63 | Assert.False(result.IsValid); 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Helper/Image/ImageHelper.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using SixLabors.ImageSharp.Processing; 3 | using SixLabors.ImageSharp.Formats; 4 | using SixLabors.ImageSharp; 5 | using System; 6 | 7 | namespace ShareBook.Helper.Image 8 | { 9 | public static class ImageHelper 10 | { 11 | public static string FormatImageName(string originalName, string slug) 12 | { 13 | var newFileName = originalName.Replace(Path.GetFileNameWithoutExtension(originalName), slug); 14 | 15 | return Path.GetFileName(newFileName); 16 | } 17 | 18 | public static string GenerateImageUrl(string imageName, string directory, string serverUrl) 19 | { 20 | return serverUrl + directory.Replace("wwwroot", "") + "/" + imageName; 21 | } 22 | 23 | /// 24 | /// Scale an image by a scale factor 25 | /// 26 | /// The image bytes 27 | /// The percentage to increase (>100) or decrease(<100) the size of the image 28 | /// The resized image as a byte[] 29 | /// 30 | public static byte[] ResizeImage(byte[] imageBytes, int scalefactor) 31 | { 32 | if (scalefactor <= 0) 33 | { 34 | throw new ArgumentException("'scalefactor' deve ser maior que 0"); 35 | } 36 | 37 | SixLabors.ImageSharp.Image image = SixLabors.ImageSharp.Image.Load(imageBytes, out IImageFormat imageFormat); 38 | 39 | var width = image.Width * scalefactor / 100; 40 | var height = image.Height * scalefactor / 100; 41 | 42 | image.Mutate(x => x.Resize(width, height)); 43 | 44 | var memoryStream = new MemoryStream(); 45 | 46 | image.Save(memoryStream, imageFormat); 47 | 48 | return memoryStream.ToArray(); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Api/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "DatabaseProvider": "sqlserver", 3 | "ConnectionStrings": { 4 | "DefaultConnection": "", 5 | "PostgresConnection": "", 6 | "SqliteConnection": "Data Source=sharebook.db;" 7 | }, 8 | "TokenConfigurations": { 9 | "Audience": "ShareBookAudience", 10 | "Issuer": "Sharebook", 11 | "Seconds": 86400, 12 | "SecretJwtKey": "" 13 | }, 14 | "Logging": { 15 | "LogLevel": { 16 | "Default": "Debug", 17 | "System": "Warning", 18 | "Microsoft": "Warning" 19 | } 20 | }, 21 | "ImageSettings": { 22 | "ImagePath": "wwwroot/Images", 23 | "EBookPdfPath": "wwwroot/EbookPdfs" 24 | }, 25 | "SharebookSettings": { 26 | "DaysInShowcase": 7, 27 | "MaxRequestsPerBook": 50, 28 | "MaxLateDonationDays": 5, 29 | "MaxLateDonationDaysAutoCancel": 10 30 | }, 31 | "ClientSettings": { 32 | "AndroidMinVersion": "v1.5.0" 33 | }, 34 | "ServerSettings": { 35 | "DefaultUrl": "https://api.sharebook.com.br", 36 | "JobExecutorToken": "" 37 | }, 38 | "EmailSettings": { 39 | "IsActive": "false", 40 | "HostName": "", 41 | "Username": "", 42 | "Password": "", 43 | "Port": 25, 44 | "UseSSL": false, 45 | "MaxEmailsPerHour": 50 46 | }, 47 | "PushNotificationSettings": { 48 | "IsActive": "false", 49 | "AppId": "", 50 | "ApiKey": "" 51 | }, 52 | "Muambator": { 53 | "IsActive": "false", 54 | "Token": "" 55 | }, 56 | "Rollbar": { 57 | "IsActive": "false", 58 | "Token": "", 59 | "Environment": "Development", 60 | "LogLevel": "Info" 61 | }, 62 | "AwsSqsSettings": { 63 | "IsActive": false, 64 | "AccessKey": "", 65 | "SecretKey": "", 66 | "Region": "", 67 | "QueueBaseUrl": "", 68 | "NewBookQueue": "new-book-dev", 69 | "SendEmailHighPriorityQueue": "send-email-high-priority-dev", 70 | "SendEmailLowPriorityQueue": "send-email-low-priority-dev" 71 | }, 72 | "MeetupSettings": { 73 | "IsActive": false, 74 | "YoutubeToken": "" 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Helper/Extensions/StringExtension.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Text; 4 | using System.Text.RegularExpressions; 5 | 6 | namespace ShareBook.Helper.Extensions 7 | { 8 | public static class StringExtension 9 | { 10 | public static string GenerateSlug(this string phrase) 11 | { 12 | string str = phrase.RemoveAccent().ToLower(); 13 | // invalid chars 14 | str = Regex.Replace(str, @"[^a-z0-9\s-]", ""); 15 | // convert multiple spaces into one space 16 | str = Regex.Replace(str, @"\s+", " ").Trim(); 17 | // cut and trim 18 | str = str.Substring(0, str.Length <= 45 ? str.Length : 45).Trim(); 19 | str = Regex.Replace(str, @"\s", "-"); // hyphens 20 | return str; 21 | } 22 | 23 | public static string RemoveAccent(this string text) 24 | { 25 | if (string.IsNullOrEmpty(text)) 26 | { 27 | return text; 28 | } 29 | 30 | var normalizedString = text.Normalize(NormalizationForm.FormD); 31 | var stringBuilder = new StringBuilder(); 32 | 33 | foreach (var c in normalizedString) 34 | { 35 | var unicodeCategory = CharUnicodeInfo.GetUnicodeCategory(c); 36 | if (unicodeCategory != UnicodeCategory.NonSpacingMark) 37 | { 38 | stringBuilder.Append(c); 39 | } 40 | } 41 | 42 | return stringBuilder.ToString().Normalize(NormalizationForm.FormC); 43 | } 44 | 45 | 46 | public static string AddIncremental(this string text) 47 | { 48 | 49 | var number = text.Split("_copy").Length == 2 ? Convert.ToInt32(text.Split("_copy")[1]) + 1 : 1; 50 | 51 | var onlyText = text.Split("_copy").Length == 2 ? text.Split("_copy")[0] : text; 52 | 53 | return $"{onlyText}_copy{number}"; 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Api/Controllers/Generic/BaseController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Cors; 2 | using Microsoft.AspNetCore.Mvc; 3 | using ShareBook.Api.Filters; 4 | using ShareBook.Api.ViewModels; 5 | using ShareBook.Domain.Common; 6 | using ShareBook.Service.Generic; 7 | using System; 8 | using System.Linq.Expressions; 9 | using System.Threading.Tasks; 10 | 11 | namespace ShareBook.Api.Controllers 12 | { 13 | public class BaseController : BaseController 14 | where T : BaseEntity 15 | { 16 | public BaseController(IBaseService service) : base(service) 17 | { 18 | } 19 | } 20 | 21 | public class BaseController : BaseController 22 | where T : BaseEntity 23 | where R : BaseViewModel 24 | { 25 | public BaseController(IBaseService service) : base(service) 26 | { 27 | } 28 | } 29 | 30 | [GetClaimsFilter] 31 | [EnableCors("AllowAllHeaders")] 32 | public class BaseController : Controller 33 | where T : BaseEntity 34 | where R : IIdProperty 35 | where A : class 36 | { 37 | protected readonly IBaseService _service; 38 | private Expression> _defaultOrder = x => x.Id; 39 | protected bool HasRequestViewModel { get { return typeof(R) != typeof(T); } } 40 | 41 | public BaseController(IBaseService service) 42 | { 43 | _service = service; 44 | } 45 | 46 | protected void SetDefault(Expression> defaultOrder) 47 | { 48 | _defaultOrder = defaultOrder; 49 | } 50 | 51 | [HttpGet()] 52 | public virtual async Task> GetAllAsync() => await PagedAsync(1, 15); 53 | 54 | [HttpGet("{page}/{items}")] 55 | public virtual async Task> PagedAsync(int page, int items) => await _service.GetAsync(x => true, _defaultOrder, page, items); 56 | 57 | [HttpGet("{id}")] 58 | public async Task GetByIdAsync(string id) => await _service.FindAsync(new Guid(id)); 59 | } 60 | } -------------------------------------------------------------------------------- /ShareBook/ShareBook.Repository/Mapping/UserMap.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using Microsoft.EntityFrameworkCore.Metadata.Builders; 3 | using ShareBook.Domain; 4 | 5 | namespace ShareBook.Repository.Mapping 6 | { 7 | public class UserMap : IEntityTypeConfiguration 8 | { 9 | public void Configure(EntityTypeBuilder entityBuilder) 10 | { 11 | entityBuilder.HasKey(t => t.Id); 12 | 13 | entityBuilder.Property(t => t.Name) 14 | .HasMaxLength(200) 15 | .IsRequired(); 16 | 17 | entityBuilder.Property(t => t.Email) 18 | .HasMaxLength(100) 19 | .IsRequired(); 20 | 21 | entityBuilder.HasIndex(t => t.Email) 22 | .IsUnique(); 23 | 24 | entityBuilder.Property(t => t.Password) 25 | .HasMaxLength(50) 26 | .IsRequired(); 27 | 28 | entityBuilder.Property(t => t.PasswordSalt) 29 | .HasMaxLength(50) 30 | .IsRequired(); 31 | 32 | entityBuilder.Property(t => t.Linkedin) 33 | .HasMaxLength(100); 34 | 35 | entityBuilder.Property(t => t.Phone) 36 | .HasMaxLength(30); 37 | 38 | entityBuilder.Property(t => t.HashCodePassword) 39 | .HasMaxLength(200); 40 | 41 | // Removido HasColumnType específico - EF Core escolhe automaticamente 42 | 43 | entityBuilder.HasMany(t => t.BooksDonated) 44 | .WithOne(b => b.User); 45 | 46 | entityBuilder.Property(t => t.Active) 47 | .HasDefaultValue(true); // Valor padrão compatível 48 | 49 | entityBuilder.Property(t => t.AllowSendingEmail) 50 | .ValueGeneratedNever() 51 | .HasDefaultValue(true); 52 | 53 | // Removido LastLogin default - será definido no código 54 | 55 | entityBuilder.Property(t => t.ParentAproved); 56 | entityBuilder.Property(t => t.ParentEmail); 57 | entityBuilder.Property(t => t.ParentHashCodeAproval); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Service/Email/Templates/ChooseDateRenewTemplate.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SHAREBOOK - É HOJE! 6 | 7 | 8 |

9 | Olá {DonorName}, 10 |

11 | 12 |

Aqui é o Sharebot do time Sharebook. Tudo bem?

13 | 14 |

Hoje seria o dia de escolher o ganhador do livro '{BookTitle}'! Mas infelizmente não apareceu nenhum 15 | interessado. Não se preocupe, renovamos data de escolha para daqui a 10 dias. Enquanto isso vamos juntos 16 | divulgar mais esse livro incrível nas mídias sociais. Tenho certeza que vamos achar MUITOS interesados. Eles só 17 | não sabem ainda. Então compartilhe o link abaixo em suas mídias sociais, grupos de whatsapp e tudo mais kkk. 18 | Bora compartilhar o conhecimento!

19 | 20 |

https://sharebook.com.br/livros/{BookSlug}

21 | 22 |

23 | Dúvidas? Sem problemas, é um prazer ajudar. Entre em contato com seu facilitador. 24 |

30 |

31 | 32 |

 

33 | 34 |

35 | Atenciosamente,

36 | 37 | Sharebook team - Compartilhando conhecimento
38 | Siga-nos no instagram: 39 | @sharebook.com.br 40 |
41 |
42 | Curta nossa fanpage pra ficar por dentro das novidades:
43 | https://www.linkedin.com/company/sharebook-br/ 44 |

45 | 46 | 47 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Api/AutoMapper/ViewModelToDomainMappingProfile.cs: -------------------------------------------------------------------------------- 1 | using AutoMapper; 2 | using ShareBook.Api.ViewModels; 3 | using ShareBook.Domain; 4 | using ShareBook.Domain.DTOs; 5 | 6 | namespace ShareBook.Api.AutoMapper 7 | { 8 | public class ViewModelToDomainMappingProfile : Profile 9 | { 10 | public ViewModelToDomainMappingProfile() : this("Profile") 11 | { 12 | } 13 | 14 | protected ViewModelToDomainMappingProfile(string profileName) : base(profileName) 15 | { 16 | #region [ Book ] 17 | 18 | CreateMap().ReverseMap(); 19 | CreateMap().ReverseMap(); 20 | CreateMap().ReverseMap(); 21 | CreateMap(); 22 | 23 | #endregion [ Book ] 24 | 25 | #region [ User ] 26 | 27 | CreateMap(); 28 | CreateMap() 29 | .ForPath(dest => dest.Address.Street, opt => opt.MapFrom(src => src.Street)) 30 | .ForPath(dest => dest.Address.Number, opt => opt.MapFrom(src => src.Number)) 31 | .ForPath(dest => dest.Address.PostalCode, opt => opt.MapFrom(src => src.PostalCode)) 32 | .ForPath(dest => dest.Address.State, opt => opt.MapFrom(src => src.State)) 33 | .ForPath(dest => dest.Address.City, opt => opt.MapFrom(src => src.City)) 34 | .ForPath(dest => dest.Address.Neighborhood, opt => opt.MapFrom(src => src.Neighborhood)) 35 | .ForPath(dest => dest.Address.Country, opt => opt.MapFrom(src => src.Country)) 36 | .ForPath(dest => dest.Address.Complement, opt => opt.MapFrom(src => src.Complement)); 37 | CreateMap(); 38 | 39 | #endregion [ User ] 40 | 41 | #region [ ContactUs ] 42 | 43 | CreateMap(); 44 | 45 | #endregion [ ContactUs ] 46 | 47 | #region [ Notification ] 48 | 49 | CreateMap(); 50 | 51 | #endregion [ Notification ] 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /ShareBook/ShareBook.Test.Unit/Mocks/BookMock.cs: -------------------------------------------------------------------------------- 1 | using ShareBook.Domain; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | 6 | namespace ShareBook.Test.Unit.Mocks 7 | { 8 | public class BookMock 9 | { 10 | public static Book GetLordTheRings(User user, User userFacilitator = null) 11 | { 12 | Guid bookId = new Guid("d9f5fde8-ee7c-4cf5-aa90-35eca3c170b9"); 13 | return new Book() 14 | { 15 | Id = bookId, 16 | Title = "Lord of the Rings", 17 | Author = "J. R. R. Tolkien", 18 | ImageSlug = "lotr.png", 19 | ImageBytes = Encoding.UTF8.GetBytes("STRINGBASE64"), 20 | User = user, 21 | BookUsers = new List { new BookUser { BookId = bookId, User = user } }, 22 | UserFacilitator = userFacilitator, 23 | CategoryId = Guid.NewGuid(), 24 | Status = ShareBook.Domain.Enums.BookStatus.Available 25 | }; 26 | } 27 | 28 | 29 | public static Book GetLordTheRings() 30 | { 31 | var requests = new List { 32 | new BookUser { BookId = new Guid("5489A967-9320-4350-E6FC-08D5CC8498F3"), UserId = new Guid("5489A967-9320-4350-E6FC-08D5CC8498F3") }, 33 | new BookUser { BookId = new Guid("5489A967-9320-4350-E6FC-08D5CC8498F3"), UserId = new Guid("5489A967-9320-4350-E6FC-08D5CC8498F3") }, 34 | new BookUser { BookId = new Guid("5489A967-9320-4350-E6FC-08D5CC8498F3"), UserId = new Guid("5489A967-9320-4350-E6FC-08D5CC8498F3") } 35 | }; 36 | 37 | return new Book() 38 | { 39 | Title = "Lord of the Rings", 40 | Author = "J. R. R. Tolkien", 41 | ImageSlug = "lotr.png", 42 | ImageBytes = Encoding.UTF8.GetBytes("STRINGBASE64"), 43 | CategoryId = Guid.NewGuid(), 44 | UserId = new Guid("5489A967-9320-4350-E6FC-08D5CC8498F3"), 45 | Status = ShareBook.Domain.Enums.BookStatus.Available, 46 | BookUsers = requests 47 | }; 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Infra.CrossCutting.Identity/ApplicationSignInManager.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.IdentityModel.Tokens; 2 | using ShareBook.Domain; 3 | using ShareBook.Infra.CrossCutting.Identity.Interfaces; 4 | using System; 5 | using System.IdentityModel.Tokens.Jwt; 6 | using System.Security.Claims; 7 | 8 | namespace ShareBook.Infra.CrossCutting.Identity 9 | { 10 | public class ApplicationSignInManager : IApplicationSignInManager 11 | { 12 | public object GenerateTokenAndSetIdentity(User user, SigningConfigurations signingConfigurations, TokenConfigurations tokenConfigurations) 13 | { 14 | ClaimsIdentity identity = new ClaimsIdentity( 15 | new[] { 16 | new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString("N")), 17 | new Claim(JwtRegisteredClaimNames.UniqueName, user.Id.ToString()), 18 | new Claim(ClaimTypes.Role, user.Profile.ToString(), ClaimValueTypes.String, tokenConfigurations.Issuer) 19 | } 20 | ); 21 | 22 | DateTime creationDate = DateTime.Now; 23 | DateTime expireDate = creationDate + TimeSpan.FromSeconds(tokenConfigurations.Seconds); 24 | 25 | var handler = new JwtSecurityTokenHandler(); 26 | var securityToken = handler.CreateToken(new SecurityTokenDescriptor 27 | { 28 | Issuer = tokenConfigurations.Issuer, 29 | Audience = tokenConfigurations.Audience, 30 | SigningCredentials = signingConfigurations.SigningCredentials, 31 | Subject = identity, 32 | NotBefore = creationDate, 33 | Expires = expireDate 34 | }); 35 | var token = handler.WriteToken(securityToken); 36 | 37 | return new 38 | { 39 | authenticated = true, 40 | created = creationDate, 41 | expiration = expireDate, 42 | accessToken = token, 43 | name = user.Name, 44 | email = user.Email, 45 | userId = user.Id, 46 | profile = user.Profile.ToString(), 47 | message = "OK" 48 | }; 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Service/Email/EmailTemplate.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Reflection; 5 | using System.Text.RegularExpressions; 6 | using System.Threading.Tasks; 7 | 8 | namespace ShareBook.Service 9 | { 10 | public class EmailTemplate : IEmailTemplate 11 | { 12 | private string TemplatesFolder = Path.Combine(AppDomain.CurrentDomain.BaseDirectory,"Email","Templates","{0}.html"); 13 | const string PropertyRegex = @"\{(.*?)\}"; 14 | private Dictionary Templates { get; set; } = new Dictionary(); 15 | 16 | private string GetPropValue(object obj, string propName) 17 | { 18 | string[] nameParts = propName.Split('.'); 19 | if (nameParts.Length == 1) 20 | { 21 | return obj.GetType().GetProperty(propName).GetValue(obj, null)?.ToString(); 22 | } 23 | 24 | foreach (string part in nameParts) 25 | { 26 | if (obj == null) { return null; } 27 | 28 | Type type = obj.GetType(); 29 | PropertyInfo info = type.GetProperty(part); 30 | if (info == null) { return null; } 31 | 32 | obj = info.GetValue(obj, null); 33 | } 34 | return obj?.ToString(); 35 | } 36 | 37 | private async Task GetTemplateAsync(string template) 38 | { 39 | if (!Templates.ContainsKey(template)) 40 | { 41 | var templatePath = string.Format(TemplatesFolder, template); 42 | Templates.Add(template, await File.ReadAllTextAsync(templatePath)); 43 | } 44 | return Templates[template]; 45 | } 46 | 47 | public async Task GenerateHtmlFromTemplateAsync(string template, object model) 48 | { 49 | var templateString = await GetTemplateAsync(template); 50 | var matches = Regex.Matches(templateString, PropertyRegex); 51 | foreach (Match item in matches) 52 | { 53 | templateString = templateString.Replace(item.Value, GetPropValue(model, item.Groups[1].Value)); 54 | } 55 | return templateString; 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Test.Unit/Jobs/4 - MeetupSearchTests.cs: -------------------------------------------------------------------------------- 1 | using Moq; 2 | using Sharebook.Jobs; 3 | using ShareBook.Service; 4 | using System.Threading.Tasks; 5 | using Xunit; 6 | using ShareBook.Repository; 7 | using Microsoft.Extensions.Configuration; 8 | using ShareBook.Domain.Enums; 9 | using System.Collections.Generic; 10 | using ShareBook.Domain; 11 | 12 | namespace ShareBook.Test.Unit.Jobs 13 | { 14 | public class MeetupSearchTests 15 | { 16 | private readonly Mock _mockJobHistoryRepository = new(); 17 | private readonly Mock _mockMeetupService = new(); 18 | private readonly Mock _mockConfiguration = new(); 19 | 20 | [Fact] 21 | public async Task MeetupSettingsDisabled_ShouldReturn_MeetupDisabled() 22 | { 23 | _mockConfiguration.SetupGet(s => s[It.IsAny()]).Returns("false"); 24 | MeetupSearch job = new MeetupSearch(_mockJobHistoryRepository.Object, _mockMeetupService.Object, _mockConfiguration.Object); 25 | 26 | JobResult result = await job.ExecuteAsync(); 27 | Assert.Equal(JobResult.MeetupDisabled, result); 28 | _mockMeetupService.VerifyNoOtherCalls(); 29 | _mockJobHistoryRepository.VerifyNoOtherCalls(); 30 | } 31 | 32 | [Fact] 33 | public async Task MeetupSettingsEnabled_ShouldReturn_MeetupsCorrectly() 34 | { 35 | List mockedMeetups = new List { "Meetup Mock 1", "Meetup Mock 2" }; 36 | _mockConfiguration.SetupGet(s => s[It.IsAny()]).Returns("true"); 37 | _mockMeetupService.Setup(s => s.FetchMeetupsAsync()).ReturnsAsync(() => mockedMeetups); 38 | MeetupSearch job = new MeetupSearch(_mockJobHistoryRepository.Object, _mockMeetupService.Object, _mockConfiguration.Object); 39 | 40 | JobHistory result = await job.WorkAsync(); 41 | Assert.Equal("MeetupSearch", result.JobName); 42 | Assert.True(result.IsSuccess); 43 | Assert.Equal(string.Join("\n", mockedMeetups), result.Details); 44 | _mockMeetupService.Verify(c => c.FetchMeetupsAsync(), Times.Once); 45 | _mockMeetupService.VerifyNoOtherCalls(); 46 | _mockJobHistoryRepository.VerifyNoOtherCalls(); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Test.Unit/Services/ContactUsEmailServiceTests.cs: -------------------------------------------------------------------------------- 1 | using Moq; 2 | using ShareBook.Domain; 3 | using ShareBook.Service; 4 | using Xunit; 5 | using System.Threading.Tasks; 6 | 7 | namespace ShareBook.Test.Unit.Services 8 | { 9 | public class ContactUsEmailServiceTests 10 | { 11 | private readonly Mock _mockEmailService = new(); 12 | private readonly Mock _mockEmailTemplate = new(); 13 | private const string HtmlMock = "Example"; 14 | 15 | public ContactUsEmailServiceTests() 16 | { 17 | _mockEmailService.Setup(t => t.SendToAdminsAsync(It.IsAny(), It.IsAny())); 18 | _mockEmailService.Setup(t => t.SendAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())); 19 | _mockEmailService.Setup(t => t.SendAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())); 20 | _mockEmailTemplate.Setup(t => t.GenerateHtmlFromTemplateAsync(It.IsAny(), It.IsAny())).ReturnsAsync(HtmlMock); 21 | } 22 | 23 | [Fact] 24 | public async Task SendEmailContactToTheUserAndToAdministrators() 25 | { 26 | ContactUs contactUs = new ContactUs 27 | { 28 | Email = "joazinho.souza@example.com", 29 | Message = "Test message test, Test message test, Test message test", 30 | Name = "Joãozinho", 31 | Phone = "44 9 8877-6655" 32 | }; 33 | ContactUsEmailService service = new ContactUsEmailService(_mockEmailService.Object, _mockEmailTemplate.Object); 34 | await service.SendEmailContactUsAsync(contactUs); 35 | 36 | _mockEmailService.Verify(s => s.SendToAdminsAsync(HtmlMock, ContactUsEmailService.ContactUsTitle), Times.Once); 37 | _mockEmailService.Verify(s => s.SendAsync(contactUs.Email, contactUs.Name, HtmlMock, ContactUsEmailService.ContactUsNotificationTitle, false, true), Times.Once); 38 | _mockEmailService.VerifyNoOtherCalls(); 39 | _mockEmailTemplate.Verify(s => s.GenerateHtmlFromTemplateAsync(It.IsAny(), It.IsAny()), Times.Exactly(2)); 40 | _mockEmailTemplate.VerifyNoOtherCalls(); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /ShareBook/ShareBook.Service/Upload/UploadService.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Options; 2 | using ShareBook.Helper.Image; 3 | using ShareBook.Service.Server; 4 | using System; 5 | using System.IO; 6 | using System.Threading.Tasks; 7 | 8 | namespace ShareBook.Service.Upload 9 | { 10 | public class UploadService : IUploadService 11 | { 12 | private readonly ImageSettings _imageSettings; 13 | private readonly ServerSettings _serverSettings; 14 | 15 | public UploadService(IOptions imageSettings, IOptions serverSettings) 16 | { 17 | _imageSettings = imageSettings.Value; 18 | _serverSettings = serverSettings.Value; 19 | } 20 | 21 | public string GetImageUrl(string imageName, string lastDirectory) 22 | { 23 | var dinamicDirectory = _imageSettings.ImagePath + "/" + lastDirectory; 24 | return ImageHelper.GenerateImageUrl(imageName, dinamicDirectory, _serverSettings.DefaultUrl); 25 | } 26 | 27 | 28 | public async Task UploadImageAsync(byte[] imageBytes, string imageName, string lastDirectory) 29 | { 30 | var dinamicDirectory = Path.Combine(_imageSettings.ImagePath, lastDirectory); 31 | 32 | await UploadFileAsync(imageBytes, imageName, dinamicDirectory); 33 | 34 | return GetImageUrl(imageName, lastDirectory); 35 | } 36 | 37 | public async Task UploadPdfAsync(byte[] imageBytes, string imageName, string lastDirectory) 38 | { 39 | var dinamicDirectory = Path.Combine(_imageSettings.EBookPdfPath, lastDirectory); 40 | 41 | await UploadFileAsync(imageBytes, imageName, dinamicDirectory); 42 | 43 | return Path.Combine(lastDirectory, dinamicDirectory.Replace("wwwroot", ""), imageName); 44 | 45 | } 46 | 47 | private static async Task UploadFileAsync(byte[] imageBytes, string imageName, string dinamicDirectory) 48 | { 49 | var directoryBase = AppDomain.CurrentDomain.BaseDirectory + dinamicDirectory; 50 | if (!Directory.Exists(directoryBase)) 51 | Directory.CreateDirectory(directoryBase); 52 | 53 | var imageCompletePath = Path.Combine(directoryBase, imageName); 54 | await File.WriteAllBytesAsync(imageCompletePath, imageBytes); 55 | } 56 | } 57 | } 58 | --------------------------------------------------------------------------------