├── .github ├── ISSUE_TEMPLATE │ ├── bug-template.md │ ├── feature-template.md │ └── refactor-template.md ├── PULL_REQUEST_TEMPLATE.md └── workflows │ └── FRONTEND_PR_TEST.yml ├── .gitmodules ├── .idea ├── .gitignore ├── 2021-pick-git.iml ├── inspectionProfiles │ └── Project_Default.xml ├── misc.xml ├── modules.xml └── uiDesigner.xml ├── README.md ├── backend ├── README.md └── pick-git │ ├── .gitignore │ ├── build.gradle │ ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ ├── script │ └── deploy_script.sh │ ├── settings.gradle │ └── src │ ├── docs │ └── asciidoc │ │ ├── authorization.adoc │ │ ├── comment.adoc │ │ ├── following.adoc │ │ ├── index.adoc │ │ ├── portfolio.adoc │ │ ├── post.adoc │ │ ├── postfeed.adoc │ │ ├── profile.adoc │ │ ├── search.adoc │ │ └── tag.adoc │ ├── main │ ├── java │ │ └── com │ │ │ └── woowacourse │ │ │ └── pickgit │ │ │ ├── PickGitApplication.java │ │ │ ├── authentication │ │ │ ├── application │ │ │ │ ├── JwtTokenProvider.java │ │ │ │ ├── OAuthService.java │ │ │ │ └── dto │ │ │ │ │ ├── OAuthProfileResponse.java │ │ │ │ │ └── TokenDto.java │ │ │ ├── domain │ │ │ │ ├── Authenticated.java │ │ │ │ ├── OAuthAccessTokenDao.java │ │ │ │ ├── OAuthClient.java │ │ │ │ └── user │ │ │ │ │ ├── AppUser.java │ │ │ │ │ ├── GuestUser.java │ │ │ │ │ └── LoginUser.java │ │ │ ├── infrastructure │ │ │ │ ├── AuthorizationExtractor.java │ │ │ │ ├── GithubOAuthClient.java │ │ │ │ ├── JwtTokenProviderImpl.java │ │ │ │ ├── dao │ │ │ │ │ ├── CollectionOAuthAccessTokenDao.java │ │ │ │ │ └── RedisOAuthAccessTokenDao.java │ │ │ │ └── dto │ │ │ │ │ ├── OAuthAccessTokenRequest.java │ │ │ │ │ └── OAuthAccessTokenResponse.java │ │ │ └── presentation │ │ │ │ ├── AuthenticationPrincipalArgumentResolver.java │ │ │ │ ├── OAuthController.java │ │ │ │ ├── dto │ │ │ │ ├── OAuthLoginUrlResponse.java │ │ │ │ └── OAuthTokenResponse.java │ │ │ │ └── interceptor │ │ │ │ ├── AuthHeader.java │ │ │ │ ├── AuthenticationInterceptor.java │ │ │ │ ├── IgnoreAuthenticationInterceptor.java │ │ │ │ └── PathMatchInterceptor.java │ │ │ ├── comment │ │ │ ├── application │ │ │ │ ├── CommentService.java │ │ │ │ └── dto │ │ │ │ │ ├── CommentDtoAssembler.java │ │ │ │ │ ├── request │ │ │ │ │ ├── CommentDeleteRequestDto.java │ │ │ │ │ ├── CommentRequestDto.java │ │ │ │ │ └── QueryCommentRequestDto.java │ │ │ │ │ └── response │ │ │ │ │ └── CommentResponseDto.java │ │ │ ├── domain │ │ │ │ ├── Comment.java │ │ │ │ ├── CommentContent.java │ │ │ │ ├── CommentRepository.java │ │ │ │ └── Comments.java │ │ │ ├── infrastructure │ │ │ │ └── empty │ │ │ └── presentation │ │ │ │ ├── CommentController.java │ │ │ │ └── dto │ │ │ │ ├── CommentAssembler.java │ │ │ │ ├── request │ │ │ │ └── ContentRequest.java │ │ │ │ └── response │ │ │ │ └── CommentResponse.java │ │ │ ├── common │ │ │ └── network │ │ │ │ └── RestTemplateClient.java │ │ │ ├── config │ │ │ ├── JpaConfiguration.java │ │ │ ├── OAuthConfiguration.java │ │ │ ├── WebClientConfiguration.java │ │ │ ├── auth_interceptor_register │ │ │ │ ├── AutoAuthorizationInterceptorRegister.java │ │ │ │ ├── ForLoginAndGuestUser.java │ │ │ │ ├── ForOnlyLoginUser.java │ │ │ │ ├── UriParser.java │ │ │ │ ├── register_type │ │ │ │ │ ├── AuthenticateStorageForRegisterType.java │ │ │ │ │ ├── IgnoreAuthenticateStorageForRegisterType.java │ │ │ │ │ ├── RegisterType.java │ │ │ │ │ └── StorageForRegisterType.java │ │ │ │ └── scanner │ │ │ │ │ ├── ControllerScanner.java │ │ │ │ │ ├── ForGuestScanner.java │ │ │ │ │ ├── ForLoginUserScanner.java │ │ │ │ │ ├── HttpMethodMapper.java │ │ │ │ │ ├── data_structure │ │ │ │ │ ├── InterceptorParameter.java │ │ │ │ │ └── MergedInterceptorParameterByMethod.java │ │ │ │ │ └── package_scanner │ │ │ │ │ ├── PackageScanner.java │ │ │ │ │ └── SourceVisitor.java │ │ │ ├── database │ │ │ │ ├── DataSourceConfiguration.java │ │ │ │ ├── ElasticSearchConfiguration.java │ │ │ │ ├── MasterDataSourceProperties.java │ │ │ │ ├── ReplicationRoutingDataSource.java │ │ │ │ └── SlaveNames.java │ │ │ └── redis │ │ │ │ ├── EmbeddedRedisConfiguration.java │ │ │ │ ├── RedisCachingConfiguration.java │ │ │ │ └── RedisConfiguration.java │ │ │ ├── exception │ │ │ ├── ApplicationException.java │ │ │ ├── GlobalExceptionHandler.java │ │ │ ├── authentication │ │ │ │ ├── AuthenticationException.java │ │ │ │ ├── InvalidTokenException.java │ │ │ │ └── UnauthorizedException.java │ │ │ ├── comment │ │ │ │ ├── CannotDeleteCommentException.java │ │ │ │ ├── CommentException.java │ │ │ │ └── CommentNotFoundException.java │ │ │ ├── dto │ │ │ │ └── ApiErrorResponse.java │ │ │ ├── platform │ │ │ │ ├── PlatformException.java │ │ │ │ ├── PlatformHttpErrorException.java │ │ │ │ └── PlatformInternalThreadException.java │ │ │ ├── portfolio │ │ │ │ ├── DuplicateProjectNameException.java │ │ │ │ ├── DuplicateSectionNameException.java │ │ │ │ ├── InvalidProjectDateException.java │ │ │ │ ├── NoSuchPortfolioException.java │ │ │ │ ├── NotYetCreatedPortfolioException.java │ │ │ │ ├── PortfolioConstraintException.java │ │ │ │ ├── PortfolioException.java │ │ │ │ └── ProjectTypeNotFoundException.java │ │ │ ├── post │ │ │ │ ├── CannotAddTagException.java │ │ │ │ ├── CannotUnlikeException.java │ │ │ │ ├── CommentFormatException.java │ │ │ │ ├── DuplicatedLikeException.java │ │ │ │ ├── FeedRequestUserParameterExtractionException.java │ │ │ │ ├── HomeFeedTypeException.java │ │ │ │ ├── IllegalSearchTypeException.java │ │ │ │ ├── NoSuchTagException.java │ │ │ │ ├── PostException.java │ │ │ │ ├── PostFormatException.java │ │ │ │ ├── PostNotBelongToUserException.java │ │ │ │ ├── PostNotFoundException.java │ │ │ │ ├── RepositoryParseException.java │ │ │ │ └── TagFormatException.java │ │ │ ├── redis │ │ │ │ └── EmbeddedRedisServerException.java │ │ │ └── user │ │ │ │ ├── ContributionParseException.java │ │ │ │ ├── DuplicateFollowException.java │ │ │ │ ├── InvalidFollowException.java │ │ │ │ ├── InvalidUserException.java │ │ │ │ ├── SameSourceTargetUserException.java │ │ │ │ ├── UserException.java │ │ │ │ └── UserNotFoundException.java │ │ │ ├── portfolio │ │ │ ├── application │ │ │ │ ├── PortfolioService.java │ │ │ │ └── dto │ │ │ │ │ ├── PortfolioDtoAssembler.java │ │ │ │ │ ├── request │ │ │ │ │ ├── ContactRequestDto.java │ │ │ │ │ ├── DescriptionRequestDto.java │ │ │ │ │ ├── ItemRequestDto.java │ │ │ │ │ ├── PortfolioRequestDto.java │ │ │ │ │ ├── ProjectRequestDto.java │ │ │ │ │ ├── SectionRequestDto.java │ │ │ │ │ ├── TagRequestDto.java │ │ │ │ │ └── UserDto.java │ │ │ │ │ └── response │ │ │ │ │ ├── ContactResponseDto.java │ │ │ │ │ ├── DescriptionResponseDto.java │ │ │ │ │ ├── ItemResponseDto.java │ │ │ │ │ ├── PortfolioResponseDto.java │ │ │ │ │ ├── ProjectResponseDto.java │ │ │ │ │ └── SectionResponseDto.java │ │ │ ├── domain │ │ │ │ ├── Portfolio.java │ │ │ │ ├── PortfolioValidator.java │ │ │ │ ├── common │ │ │ │ │ ├── Updatable.java │ │ │ │ │ ├── UpdatableProxy.java │ │ │ │ │ └── UpdateUtil.java │ │ │ │ ├── contact │ │ │ │ │ ├── Contact.java │ │ │ │ │ └── Contacts.java │ │ │ │ ├── project │ │ │ │ │ ├── Project.java │ │ │ │ │ ├── ProjectTag.java │ │ │ │ │ ├── ProjectType.java │ │ │ │ │ └── Projects.java │ │ │ │ ├── repository │ │ │ │ │ └── PortfolioRepository.java │ │ │ │ └── section │ │ │ │ │ ├── Section.java │ │ │ │ │ ├── Sections.java │ │ │ │ │ └── item │ │ │ │ │ ├── Description.java │ │ │ │ │ └── Item.java │ │ │ ├── infrastructure │ │ │ │ └── empty.txt │ │ │ └── presentation │ │ │ │ ├── PortfolioController.java │ │ │ │ └── dto │ │ │ │ ├── PortfolioAssembler.java │ │ │ │ ├── request │ │ │ │ ├── ContactRequest.java │ │ │ │ ├── DescriptionRequest.java │ │ │ │ ├── ItemRequest.java │ │ │ │ ├── PortfolioRequest.java │ │ │ │ ├── ProjectRequest.java │ │ │ │ ├── SectionRequest.java │ │ │ │ └── TagRequest.java │ │ │ │ └── response │ │ │ │ ├── ContactResponse.java │ │ │ │ ├── DescriptionResponse.java │ │ │ │ ├── ItemResponse.java │ │ │ │ ├── PortfolioResponse.java │ │ │ │ ├── ProjectResponse.java │ │ │ │ └── SectionResponse.java │ │ │ ├── post │ │ │ ├── application │ │ │ │ ├── PostFeedService.java │ │ │ │ ├── PostService.java │ │ │ │ ├── dto │ │ │ │ │ ├── PostDtoAssembler.java │ │ │ │ │ ├── request │ │ │ │ │ │ ├── AuthUserForPostRequestDto.java │ │ │ │ │ │ ├── HomeFeedRequestDto.java │ │ │ │ │ │ ├── PostDeleteRequestDto.java │ │ │ │ │ │ ├── PostRequestDto.java │ │ │ │ │ │ ├── PostUpdateRequestDto.java │ │ │ │ │ │ ├── RepositoryRequestDto.java │ │ │ │ │ │ ├── SearchPostRequestDto.java │ │ │ │ │ │ ├── SearchPostsRequestDto.java │ │ │ │ │ │ └── SearchRepositoryRequestDto.java │ │ │ │ │ └── response │ │ │ │ │ │ ├── LikeResponseDto.java │ │ │ │ │ │ ├── LikeUsersResponseDto.java │ │ │ │ │ │ ├── PostImageUrlResponseDto.java │ │ │ │ │ │ ├── PostResponseDto.java │ │ │ │ │ │ ├── PostUpdateResponseDto.java │ │ │ │ │ │ ├── RepositoryResponseDto.java │ │ │ │ │ │ └── RepositoryResponsesDto.java │ │ │ │ └── search │ │ │ │ │ ├── SearchTypes.java │ │ │ │ │ └── type │ │ │ │ │ ├── SearchType.java │ │ │ │ │ └── TagType.java │ │ │ ├── domain │ │ │ │ ├── Post.java │ │ │ │ ├── Posts.java │ │ │ │ ├── content │ │ │ │ │ ├── Image.java │ │ │ │ │ ├── Images.java │ │ │ │ │ └── PostContent.java │ │ │ │ ├── like │ │ │ │ │ ├── Like.java │ │ │ │ │ └── Likes.java │ │ │ │ ├── repository │ │ │ │ │ ├── PickGitStorage.java │ │ │ │ │ └── PostRepository.java │ │ │ │ ├── tag │ │ │ │ │ ├── PostTag.java │ │ │ │ │ └── PostTags.java │ │ │ │ └── util │ │ │ │ │ ├── PlatformRepositoryApiRequester.java │ │ │ │ │ ├── PlatformRepositoryExtractor.java │ │ │ │ │ ├── PlatformRepositorySearchExtractor.java │ │ │ │ │ ├── RestClient.java │ │ │ │ │ └── dto │ │ │ │ │ ├── RepositoryNameAndUrl.java │ │ │ │ │ └── SearchRepositoryNameAndUrl.java │ │ │ ├── infrastructure │ │ │ │ ├── S3Storage.java │ │ │ │ ├── dto │ │ │ │ │ └── RepositoryItemDto.java │ │ │ │ ├── extractor │ │ │ │ │ ├── GithubRepositoryExtractor.java │ │ │ │ │ └── GithubRepositorySearchExtractor.java │ │ │ │ └── requester │ │ │ │ │ └── GithubRepositoryApiRequester.java │ │ │ └── presentation │ │ │ │ ├── PostController.java │ │ │ │ ├── PostFeedController.java │ │ │ │ ├── dto │ │ │ │ ├── PostAssembler.java │ │ │ │ ├── request │ │ │ │ │ ├── PostRequest.java │ │ │ │ │ ├── PostUpdateRequest.java │ │ │ │ │ └── SearchPostsRequest.java │ │ │ │ └── response │ │ │ │ │ ├── LikeResponse.java │ │ │ │ │ ├── LikeUsersResponse.java │ │ │ │ │ ├── PostResponse.java │ │ │ │ │ ├── PostUpdateResponse.java │ │ │ │ │ └── RepositoryResponse.java │ │ │ │ └── postfeed │ │ │ │ ├── AllFeedType.java │ │ │ │ ├── FeedType.java │ │ │ │ ├── FeedTypeAuthorizator.java │ │ │ │ └── FollowingsFeedType.java │ │ │ ├── tag │ │ │ ├── application │ │ │ │ ├── TagService.java │ │ │ │ └── dto │ │ │ │ │ ├── ExtractionRequestDto.java │ │ │ │ │ └── TagsDto.java │ │ │ ├── domain │ │ │ │ ├── PlatformTagExtractor.java │ │ │ │ ├── Tag.java │ │ │ │ └── TagRepository.java │ │ │ ├── infrastructure │ │ │ │ ├── GithubTagApiRequester.java │ │ │ │ ├── GithubTagExtractor.java │ │ │ │ └── PlatformTagApiRequester.java │ │ │ └── presentation │ │ │ │ ├── TagController.java │ │ │ │ └── dto │ │ │ │ └── TagAssembler.java │ │ │ └── user │ │ │ ├── application │ │ │ ├── UserService.java │ │ │ └── dto │ │ │ │ ├── UserDtoAssembler.java │ │ │ │ ├── request │ │ │ │ ├── AuthUserForUserRequestDto.java │ │ │ │ ├── FollowRequestDto.java │ │ │ │ ├── FollowSearchRequestDto.java │ │ │ │ ├── ProfileImageEditRequestDto.java │ │ │ │ └── UserSearchRequestDto.java │ │ │ │ └── response │ │ │ │ ├── ContributionResponseDto.java │ │ │ │ ├── FollowResponseDto.java │ │ │ │ ├── ProfileImageEditResponseDto.java │ │ │ │ ├── UserProfileResponseDto.java │ │ │ │ └── UserSearchResponseDto.java │ │ │ ├── domain │ │ │ ├── User.java │ │ │ ├── contribution │ │ │ │ ├── Contribution.java │ │ │ │ ├── ContributionCategory.java │ │ │ │ └── PlatformContributionCalculator.java │ │ │ ├── dto │ │ │ │ └── ContributionDto.java │ │ │ ├── follow │ │ │ │ ├── Follow.java │ │ │ │ ├── Followers.java │ │ │ │ ├── Followings.java │ │ │ │ └── PlatformFollowingRequester.java │ │ │ ├── profile │ │ │ │ ├── BasicProfile.java │ │ │ │ ├── GithubProfile.java │ │ │ │ └── PickGitProfileStorage.java │ │ │ ├── repository │ │ │ │ └── UserRepository.java │ │ │ └── search │ │ │ │ ├── CustomUserSearchEngine.java │ │ │ │ ├── CustomUserSearchEngineImpl.java │ │ │ │ └── UserSearchEngine.java │ │ │ ├── infrastructure │ │ │ ├── S3PickGitProfileStorage.java │ │ │ ├── contribution │ │ │ │ ├── GithubContributionCalculator.java │ │ │ │ ├── GithubContributionExtractor.java │ │ │ │ └── PlatformContributionExtractor.java │ │ │ ├── dto │ │ │ │ ├── CountDto.java │ │ │ │ ├── ItemDto.java │ │ │ │ └── StarsDto.java │ │ │ └── requester │ │ │ │ └── GithubFollowingRequester.java │ │ │ └── presentation │ │ │ ├── UserController.java │ │ │ ├── UserSearchController.java │ │ │ └── dto │ │ │ ├── UserAssembler.java │ │ │ ├── request │ │ │ ├── ContributionRequestDto.java │ │ │ └── ProfileDescriptionRequest.java │ │ │ └── response │ │ │ ├── ContributionResponse.java │ │ │ ├── FollowResponse.java │ │ │ ├── ProfileDescriptionResponse.java │ │ │ ├── ProfileImageEditResponse.java │ │ │ ├── UserProfileResponse.java │ │ │ └── UserSearchResponse.java │ └── resources │ │ ├── access │ │ ├── access-logger.xml │ │ └── test-access-logger.xml │ │ ├── application-test.yml │ │ ├── application.yml │ │ ├── binary │ │ └── redis │ │ │ └── redis-server-6.2.5-mac-arm64 │ │ ├── db │ │ └── migration │ │ │ ├── V1__init.sql │ │ │ ├── V2__add_portfolio.sql │ │ │ ├── V3__add_user_to_portfolio.sql │ │ │ ├── V4__add_created_at_and_updated_at_to_portfolio.sql │ │ │ ├── V5__add_name_to_portfolio.sql │ │ │ ├── V6__add_unique_constraint_to_project_tag.sql │ │ │ ├── V7__add_unique_constraint_to_user_name.sql │ │ │ └── V8__update_portfolio_column_type.sql │ │ ├── log4j2 │ │ ├── log4j2-dev.xml │ │ ├── log4j2-local.xml │ │ ├── log4j2-prod.xml │ │ └── log4j2-test.xml │ │ └── logback-access.xml │ └── test │ ├── java │ └── com │ │ └── woowacourse │ │ └── pickgit │ │ ├── acceptance │ │ ├── AcceptanceTest.java │ │ ├── authentication │ │ │ └── OAuthAcceptanceTest.java │ │ ├── comment │ │ │ ├── CommentCreateAcceptanceTest.java │ │ │ ├── CommentDeleteAcceptanceTest.java │ │ │ └── CommentReadAcceptanceTest.java │ │ ├── portfolio │ │ │ ├── PortfolioConstraintsAcceptanceTest.java │ │ │ ├── PortfolioReadAcceptanceTest.java │ │ │ └── PortfolioUpdateAcceptanceTest.java │ │ ├── post │ │ │ ├── PostCreateAcceptanceTest.java │ │ │ ├── PostDeleteAcceptanceTest.java │ │ │ ├── PostReadAcceptanceTest.java │ │ │ ├── PostSearchAcceptanceTest.java │ │ │ ├── PostUpdateAcceptanceTest.java │ │ │ ├── github │ │ │ │ └── PostRepositorySearchAcceptanceTest.java │ │ │ └── like │ │ │ │ ├── PostLikeCreateAcceptanceTest.java │ │ │ │ ├── PostLikeDeleteAcceptanceTest.java │ │ │ │ └── PostLikeReadAcceptanceTest.java │ │ ├── tag │ │ │ └── TagExtractionAcceptanceTest.java │ │ └── user │ │ │ ├── UserContributionAcceptanceTest.java │ │ │ ├── UserProfileReadAcceptanceTest.java │ │ │ ├── UserProfileUpdateAcceptanceTest.java │ │ │ ├── UserSearchAcceptanceTest.java │ │ │ └── follow │ │ │ ├── UserFollowCreateAcceptanceTest.java │ │ │ ├── UserFollowDeleteAcceptanceTest.java │ │ │ └── UserFollowReadAcceptanceTest.java │ │ ├── common │ │ ├── PickgitHeaders.java │ │ ├── factory │ │ │ ├── FileFactory.java │ │ │ ├── MockPost.java │ │ │ ├── MockUser.java │ │ │ ├── PortfolioFactory.java │ │ │ ├── PostFactory.java │ │ │ └── UserFactory.java │ │ ├── fixture │ │ │ ├── Act.java │ │ │ ├── AllUserAct.java │ │ │ ├── CPost.java │ │ │ ├── LoginAndThenAct.java │ │ │ ├── TContact.java │ │ │ ├── TDescription.java │ │ │ ├── TItem.java │ │ │ ├── TPortfolio.java │ │ │ ├── TPost.java │ │ │ ├── TProject.java │ │ │ ├── TRepository.java │ │ │ ├── TSection.java │ │ │ ├── TUser.java │ │ │ └── UnLoginAndThenAct.java │ │ └── mockapi │ │ │ ├── MockContributionCalculator.java │ │ │ ├── MockGithubOAuthClient.java │ │ │ ├── MockPickGitProfileStorage.java │ │ │ ├── MockPickGitStorage.java │ │ │ ├── MockPlatformFollowingRequester.java │ │ │ ├── MockRepositoryApiRequester.java │ │ │ └── MockTagApiRequester.java │ │ ├── config │ │ ├── .DS_Store │ │ ├── DatabaseConfigurator.java │ │ ├── InfrastructureTestConfiguration.java │ │ ├── JpaTestConfiguration.java │ │ ├── RedisCachingTestConfiguration.java │ │ ├── RedisCleaner.java │ │ ├── SearchEngineCleaner.java │ │ ├── auth_interceptor_register │ │ │ ├── AutoAuthorizationInterceptorRegisterTest.java │ │ │ ├── UriParserTest.java │ │ │ └── scanner │ │ │ │ ├── ControllerScannerTest.java │ │ │ │ ├── ForLoginAndGuestUserScannerTest.java │ │ │ │ ├── ForOnlyLoginUserScannerTest.java │ │ │ │ ├── HttpMethodMapperTest.java │ │ │ │ ├── PackageScannerTest.java │ │ │ │ ├── SourceVisitorTest.java │ │ │ │ └── test_classes │ │ │ │ ├── ClassOne.java │ │ │ │ ├── ClassThree.java │ │ │ │ ├── ClassTwo.java │ │ │ │ └── inner │ │ │ │ └── ClassFour.java │ │ ├── count_data_source │ │ │ ├── Count.java │ │ │ ├── CountDataSource.java │ │ │ ├── ProxyConnectionHandler.java │ │ │ └── QueryCounter.java │ │ └── db │ │ │ ├── DataSourceConfiguration.java │ │ │ ├── DataSourceSelector.java │ │ │ ├── ReplicationRoutingDataSource.java │ │ │ └── SchemaGenerator.java │ │ ├── docs │ │ └── ApiDocumentUtils.java │ │ ├── integration │ │ ├── IntegrationTest.java │ │ ├── authentication │ │ │ ├── AuthenticationInterceptorIntegrationTest.java │ │ │ ├── AuthenticationPrincipalArgumentResolverIntegrationTest.java │ │ │ ├── IgnoreAuthenticationInterceptorIntegrationTest.java │ │ │ └── OAuthServiceIntegrationTest.java │ │ ├── comment │ │ │ ├── CommentServiceIntegrationTest.java │ │ │ ├── CommentServiceIntegrationTest_delete.java │ │ │ └── CommentServiceIntegrationTest_queryComments.java │ │ ├── post │ │ │ ├── PostFeedCacheIntegrationTest.java │ │ │ ├── PostFeedServiceIntegrationTest.java │ │ │ ├── PostFeedServiceIntegrationTest_Query.java │ │ │ ├── PostFeedServiceIntegrationTest_SearchById.java │ │ │ ├── PostFeedServiceIntegrationTest_search.java │ │ │ ├── PostServiceIntegrationTest.java │ │ │ └── PostServiceIntegrationTest_likeUsers.java │ │ ├── tag │ │ │ └── TagServiceIntegrationTest.java │ │ └── user │ │ │ ├── UserServiceIntegrationTest.java │ │ │ └── UserServiceIntegrationTest_Query.java │ │ └── unit │ │ ├── ControllerTest.java │ │ ├── authentication │ │ ├── application │ │ │ └── OAuthServiceTest.java │ │ ├── dao │ │ │ └── OAuthAccessTokenDaoTest.java │ │ └── presentation │ │ │ └── OAuthControllerTest.java │ │ ├── comment │ │ ├── application │ │ │ ├── CommentServiceTest.java │ │ │ ├── CommentServiceTest_delete.java │ │ │ └── CommentServiceTest_queryComments.java │ │ ├── domain │ │ │ └── CommentTest.java │ │ └── presentation │ │ │ ├── CommentControllerTest.java │ │ │ ├── CommentControllerTest_delete.java │ │ │ └── CommentControllerTest_queryComments.java │ │ ├── portfolio │ │ └── presentation │ │ │ └── PortfolioControllerTest.java │ │ ├── post │ │ ├── application │ │ │ ├── PostFeedServiceTest.java │ │ │ ├── PostFeedService_search.java │ │ │ ├── PostServiceTest.java │ │ │ ├── PostServiceTest_likeUsers.java │ │ │ ├── SearchTypesTest.java │ │ │ └── TagTypeTest.java │ │ ├── domain │ │ │ ├── PostRepositoryTest.java │ │ │ ├── PostRepositoryTest_likeUsers.java │ │ │ ├── PostRepositoryTest_search.java │ │ │ ├── PostTest.java │ │ │ ├── PostsTest.java │ │ │ ├── content │ │ │ │ ├── ImageTest.java │ │ │ │ ├── ImagesTest.java │ │ │ │ └── PostContentTest.java │ │ │ └── like │ │ │ │ ├── LikeTest.java │ │ │ │ └── LikesTest.java │ │ ├── infrastructure │ │ │ ├── GithubRepositoryApiRequesterTest.java │ │ │ ├── GithubRepositoryExtractorTest.java │ │ │ ├── GithubRepositorySearchExtractorTest.java │ │ │ ├── S3StorageTest.java │ │ │ └── StubRestClient.java │ │ └── presentation │ │ │ ├── PostControllerTest.java │ │ │ ├── PostControllerTest_likeUsers.java │ │ │ ├── PostFeedControllerTest.java │ │ │ └── PostFeedControllerTest_searchPosts.java │ │ ├── tag │ │ ├── application │ │ │ └── TagServiceTest.java │ │ ├── domain │ │ │ ├── TagRepositoryTest.java │ │ │ └── TagTest.java │ │ ├── infrastructure │ │ │ └── GithubTagExtractorTest.java │ │ └── presentation │ │ │ └── TagControllerTest.java │ │ └── user │ │ ├── application │ │ └── UserServiceTest.java │ │ ├── domain │ │ ├── FollowTest.java │ │ ├── FollowersTest.java │ │ ├── FollowingsTest.java │ │ ├── UserRepositoryTest.java │ │ └── UserTest.java │ │ ├── infrastructure │ │ └── GithubContributionCalculatorTest.java │ │ └── presentation │ │ ├── UserControllerTest.java │ │ └── UserSearchControllerTest.java │ └── resources │ ├── testImage1.png │ ├── testImage2.png │ └── testjar.jar ├── frontend ├── .eslintrc ├── .gitignore ├── .prettierrc ├── .storybook │ ├── main.js │ ├── preview.js │ └── utils │ │ ├── LoggedInWrapper.tsx │ │ └── components.tsx ├── README.md ├── babel.config.ts ├── jest.config.ts ├── jestSetup.ts ├── package.json ├── public │ ├── favicon.ico │ └── index.html ├── script │ └── deploy_script.sh ├── src │ ├── @types │ │ ├── assets.d.ts │ │ ├── external.d.ts │ │ ├── index.ts │ │ └── react-app-env.d.ts │ ├── App.style.ts │ ├── App.tsx │ ├── assets │ │ ├── font │ │ │ ├── NanumBarunGothic.ttf │ │ │ ├── NanumBarunGothicBold.ttf │ │ │ ├── NanumGothic.ttf │ │ │ ├── NanumGothicBold.ttf │ │ │ └── NotoSansKR-Regular.ttf │ │ ├── icons │ │ │ ├── add-box.svg │ │ │ ├── add-circle-large.svg │ │ │ ├── add-circle.svg │ │ │ ├── arrow-down.svg │ │ │ ├── arrow-right.svg │ │ │ ├── blog.svg │ │ │ ├── book.svg │ │ │ ├── briefcase-solid.svg │ │ │ ├── camera.svg │ │ │ ├── cancel-no-circle.svg │ │ │ ├── clock.svg │ │ │ ├── company-line.svg │ │ │ ├── copy.svg │ │ │ ├── delete-circle.svg │ │ │ ├── delete.svg │ │ │ ├── edit.svg │ │ │ ├── email.svg │ │ │ ├── github-brands.svg │ │ │ ├── github-large.svg │ │ │ ├── github-line.svg │ │ │ ├── github-repository.svg │ │ │ ├── github.svg │ │ │ ├── go-back.svg │ │ │ ├── go-down.svg │ │ │ ├── go-forward.svg │ │ │ ├── heart-line.svg │ │ │ ├── heart.svg │ │ │ ├── home.svg │ │ │ ├── index.tsx │ │ │ ├── issue.svg │ │ │ ├── kakao.svg │ │ │ ├── link-solid.svg │ │ │ ├── link.svg │ │ │ ├── location-line.svg │ │ │ ├── login.svg │ │ │ ├── logo-large.svg │ │ │ ├── logo.svg │ │ │ ├── map-marker-alt-solid.svg │ │ │ ├── person.svg │ │ │ ├── phone.svg │ │ │ ├── post-heart-line.svg │ │ │ ├── post-heart.svg │ │ │ ├── pr.svg │ │ │ ├── search.svg │ │ │ ├── send.svg │ │ │ ├── share.svg │ │ │ ├── star.svg │ │ │ ├── trash.svg │ │ │ ├── twitter-brands.svg │ │ │ ├── upload.svg │ │ │ ├── vertical-dots.svg │ │ │ └── website.svg │ │ └── images │ │ │ ├── app-background.png │ │ │ ├── cannot-find.png │ │ │ ├── comment-not-found.jpg │ │ │ ├── default-image.png │ │ │ ├── default-profile.png │ │ │ ├── empty-post-image.png │ │ │ ├── post-not-found.png │ │ │ └── user-not-found.png │ ├── components │ │ ├── @layout │ │ │ ├── AlertPortal │ │ │ │ ├── AlertPortal.stories.tsx │ │ │ │ ├── AlertPortal.styles.tsx │ │ │ │ └── AlertPortal.tsx │ │ │ ├── BottomSliderPortal │ │ │ │ ├── BottomSliderPortal.stories.tsx │ │ │ │ ├── BottomSliderPortal.styles.tsx │ │ │ │ └── BottomSliderPortal.tsx │ │ │ ├── ChoiceModalPortal │ │ │ │ ├── ChoiceModalPortal.stories.tsx │ │ │ │ ├── ChoiceModalPortal.styles.tsx │ │ │ │ └── ChoiceModalPortal.tsx │ │ │ ├── ConfirmPortal │ │ │ │ ├── ConfirmPortal.stories.tsx │ │ │ │ ├── ConfirmPortal.styles.tsx │ │ │ │ └── ConfirmPortal.tsx │ │ │ ├── ModalPortal │ │ │ │ ├── ModalPortal.stories.tsx │ │ │ │ ├── ModalPortal.style.ts │ │ │ │ └── ModalPortal.tsx │ │ │ ├── NavigationHeader │ │ │ │ ├── NavigationHeader.stories.tsx │ │ │ │ ├── NavigationHeader.style.ts │ │ │ │ └── NavigationHeader.tsx │ │ │ ├── PageLoading │ │ │ │ ├── PageLoading.stories.tsx │ │ │ │ ├── PageLoading.style.ts │ │ │ │ └── PageLoading.tsx │ │ │ ├── PageLoadingWithCover │ │ │ │ ├── PageLoadingWithCover.stories.tsx │ │ │ │ ├── PageLoadingWithCover.style.ts │ │ │ │ └── PageLoadingWithCover.tsx │ │ │ ├── PageLoadingWithLogo │ │ │ │ ├── PageLoadingWithLogo.stories.tsx │ │ │ │ ├── PageLoadingWithLogo.style.ts │ │ │ │ └── PageLoadingWithLogo.tsx │ │ │ ├── PortfolioHeader │ │ │ │ ├── PortfolioHeader.stories.tsx │ │ │ │ ├── PortfolioHeader.style.ts │ │ │ │ └── PortfolioHeader.tsx │ │ │ ├── ScrollActiveHeader │ │ │ │ ├── ScrollActiveHeader.stories.tsx │ │ │ │ ├── ScrollActiveHeader.style.ts │ │ │ │ └── ScrollActiveHeader.tsx │ │ │ ├── SearchHeader │ │ │ │ ├── SearchHeader.stories.tsx │ │ │ │ ├── SearchHeader.style.ts │ │ │ │ └── SearchHeader.tsx │ │ │ ├── Snackbar │ │ │ │ ├── Snackbar.stories.tsx │ │ │ │ ├── Snackbar.style.ts │ │ │ │ └── Snackbar.tsx │ │ │ └── StepHeader │ │ │ │ ├── StepHeader.stories.tsx │ │ │ │ ├── StepHeader.style.ts │ │ │ │ └── StepHeader.tsx │ │ ├── @shared │ │ │ ├── Avatar │ │ │ │ ├── Avatar.stories.tsx │ │ │ │ ├── Avatar.style.ts │ │ │ │ └── Avatar.tsx │ │ │ ├── Button │ │ │ │ ├── Button.stories.tsx │ │ │ │ ├── Button.style.ts │ │ │ │ └── Button.tsx │ │ │ ├── ButtonDrawer │ │ │ │ ├── ButtonDrawer.stories.tsx │ │ │ │ ├── ButtonDrawer.style.ts │ │ │ │ └── ButtonDrawer.tsx │ │ │ ├── Chip │ │ │ │ ├── Chip.stories.tsx │ │ │ │ ├── Chip.style.ts │ │ │ │ └── Chip.tsx │ │ │ ├── CircleIcon │ │ │ │ ├── CircleIcon.stories.tsx │ │ │ │ ├── CircleIcon.style.ts │ │ │ │ └── CircleIcon.tsx │ │ │ ├── Comment │ │ │ │ ├── Comment.stories.tsx │ │ │ │ ├── Comment.style.ts │ │ │ │ └── Comment.tsx │ │ │ ├── ContributionGraph │ │ │ │ ├── ContributionGraph.stories.tsx │ │ │ │ ├── ContributionGraph.style.ts │ │ │ │ └── ContributionGraph.tsx │ │ │ ├── CountIndicator │ │ │ │ ├── CountIndicator.stories.tsx │ │ │ │ ├── CountIndicator.style.ts │ │ │ │ └── CountIndicator.tsx │ │ │ ├── DateInput │ │ │ │ ├── DateInput.stories.tsx │ │ │ │ ├── DateInput.style.ts │ │ │ │ └── DateInput.tsx │ │ │ ├── DotPaginator │ │ │ │ ├── DotPaginator.stories.tsx │ │ │ │ ├── DotPaginator.style.ts │ │ │ │ └── DotPaginator.tsx │ │ │ ├── DropDown │ │ │ │ ├── DropDown.stories.tsx │ │ │ │ ├── DropDown.style.ts │ │ │ │ └── DropDown.tsx │ │ │ ├── Fab │ │ │ │ ├── Fab.stories.tsx │ │ │ │ ├── Fab.style.ts │ │ │ │ └── Fab.tsx │ │ │ ├── GridFeed │ │ │ │ ├── GridFeed.stories.tsx │ │ │ │ ├── GridFeed.styled.ts │ │ │ │ └── GridFeed.tsx │ │ │ ├── ImageSlider │ │ │ │ ├── ImageSlider.stories.tsx │ │ │ │ ├── ImageSlider.style.ts │ │ │ │ └── ImageSlider.tsx │ │ │ ├── ImageUploader │ │ │ │ ├── ImageUploader.stories.tsx │ │ │ │ ├── ImageUploader.style.ts │ │ │ │ └── ImageUploader.tsx │ │ │ ├── InfiniteScrollContainer │ │ │ │ ├── InfiniteScrollContainer.stories.tsx │ │ │ │ ├── InfiniteScrollContainer.style.ts │ │ │ │ └── InfiniteScrollContainer.tsx │ │ │ ├── Input │ │ │ │ ├── Input.stories.tsx │ │ │ │ ├── Input.style.ts │ │ │ │ └── Input.tsx │ │ │ ├── Loader │ │ │ │ ├── Loader.stories.tsx │ │ │ │ ├── Loader.style.ts │ │ │ │ └── Loader.tsx │ │ │ ├── NotFound │ │ │ │ ├── NotFound.stories.tsx │ │ │ │ ├── NotFound.style.ts │ │ │ │ └── NotFound.tsx │ │ │ ├── PageError │ │ │ │ ├── PageError.stories.tsx │ │ │ │ ├── PageError.style.ts │ │ │ │ └── PageError.tsx │ │ │ ├── PostItem │ │ │ │ ├── PostItem.stories.tsx │ │ │ │ ├── PostItem.style.ts │ │ │ │ └── PostItem.tsx │ │ │ ├── ProfileHeader │ │ │ │ ├── ProfileHeader.stories.tsx │ │ │ │ ├── ProfileHeader.style.ts │ │ │ │ └── ProfileHeader.tsx │ │ │ ├── SVGIcon │ │ │ │ └── SVGIcon.tsx │ │ │ ├── ShareLink │ │ │ │ ├── ShareLink.style.tsx │ │ │ │ └── ShareLink.tsx │ │ │ ├── SliderHeader │ │ │ │ ├── SliderHeader.stories.tsx │ │ │ │ ├── SliderHeader.style.ts │ │ │ │ └── SliderHeader.tsx │ │ │ ├── Tabs │ │ │ │ ├── Tabs.stories.tsx │ │ │ │ ├── Tabs.style.ts │ │ │ │ └── Tabs.tsx │ │ │ ├── TextEditor │ │ │ │ ├── TextEditor.stories.tsx │ │ │ │ ├── TextEditor.style.ts │ │ │ │ └── TextEditor.tsx │ │ │ └── ToggleButton │ │ │ │ ├── ToggleButton.stories.tsx │ │ │ │ ├── ToggleButton.style.ts │ │ │ │ └── ToggleButton.tsx │ │ ├── @styled │ │ │ ├── BackDrop.tsx │ │ │ ├── keyframes.ts │ │ │ ├── layout.tsx │ │ │ ├── mediaQueries.ts │ │ │ └── scrollbar.ts │ │ ├── Feed │ │ │ ├── Feed.stories.tsx │ │ │ ├── Feed.style.ts │ │ │ └── Feed.tsx │ │ ├── GithubStatistics │ │ │ ├── GithubStatistics.stories.tsx │ │ │ ├── GithubStatistics.style.ts │ │ │ └── GithubStatistics.tsx │ │ ├── OneDepthStepHeader │ │ │ └── OneDepthStepHeader.tsx │ │ ├── PortfolioContactForm │ │ │ ├── PortfolioContactForm.stories.tsx │ │ │ ├── PortfolioContactForm.style.ts │ │ │ └── PortfolioContactForm.tsx │ │ ├── PortfolioDocument │ │ │ ├── PortfolioDocument.style.ts │ │ │ └── PortfolioDocument.tsx │ │ ├── PortfolioProjectSection │ │ │ ├── PortfolioProjectSection.stories.tsx │ │ │ ├── PortfolioProjectSection.style.ts │ │ │ └── PortfolioProjectSection.tsx │ │ ├── PortfolioSection │ │ │ ├── PortfolioSection.stories.tsx │ │ │ ├── PortfolioSection.style.ts │ │ │ └── PortfolioSection.tsx │ │ ├── PortfolioTextEditor │ │ │ ├── PortfolioTextEditor.stories.tsx │ │ │ ├── PortfolioTextEditor.style.ts │ │ │ └── PortfolioTextEditor.tsx │ │ ├── PostAddStepHeader │ │ │ ├── PostAddStepHeader.stories.tsx │ │ │ ├── PostAddStepHeader.style.tsx │ │ │ └── PostAddStepHeader.tsx │ │ ├── PostContentUploader │ │ │ ├── PostContentUploader.stories.tsx │ │ │ ├── PostContentUploader.style.tsx │ │ │ └── PostContentUploader.tsx │ │ ├── PostSelector │ │ │ ├── PostSelector.stories.tsx │ │ │ ├── PostSelector.style.ts │ │ │ └── PostSelector.tsx │ │ ├── PostTextEditor │ │ │ ├── PostTextEditor.stories.tsx │ │ │ ├── PostTextEditor.style.ts │ │ │ └── PostTextEditor.tsx │ │ ├── Profile │ │ │ ├── Profile.stories.tsx │ │ │ ├── Profile.style.tsx │ │ │ └── Profile.tsx │ │ ├── ProfileModificationForm │ │ │ ├── ProfileModificationForm.stories.tsx │ │ │ ├── ProfileModificationForm.style.ts │ │ │ └── ProfileModificationForm.tsx │ │ ├── ProfileTabContents │ │ │ ├── ProfileTabContents.stories.tsx │ │ │ ├── ProfileTabContents.style.ts │ │ │ └── ProfileTabContents.tsx │ │ ├── RepositorySelector │ │ │ ├── RepositorySelector.stories.tsx │ │ │ ├── RepositorySelector.style.tsx │ │ │ └── RepositorySelector.tsx │ │ ├── TagInputForm │ │ │ ├── TagInputForm.stories.tsx │ │ │ ├── TagInputForm.style.tsx │ │ │ └── TagInputForm.tsx │ │ └── UserList │ │ │ ├── UserList.stories.tsx │ │ │ ├── UserList.style.ts │ │ │ └── UserList.tsx │ ├── constants │ │ ├── animation.ts │ │ ├── error.ts │ │ ├── kakao.ts │ │ ├── layout.ts │ │ ├── limits.ts │ │ ├── localStorageKey.ts │ │ ├── messages.ts │ │ ├── placeholder.ts │ │ ├── portfolio.ts │ │ ├── queries.ts │ │ ├── snackbar.ts │ │ ├── steps.ts │ │ └── urls.ts │ ├── contexts │ │ ├── HomeFeedContext.tsx │ │ ├── PostAddDataContext.tsx │ │ ├── PostAddStepContext.tsx │ │ ├── PostEditDataContext.tsx │ │ ├── PostEditStepContext.tsx │ │ ├── SearchContext.tsx │ │ ├── SearchPostContext.tsx │ │ ├── SnackbarContext.tsx │ │ ├── UserContext.tsx │ │ └── UserFeedContext.tsx │ ├── hooks │ │ ├── @mocks │ │ │ ├── mockFollow.tsx │ │ │ ├── mockFollowerList.tsx │ │ │ ├── mockFollowingList.tsx │ │ │ ├── mockGithubStatistics.tsx │ │ │ ├── mockProfile.tsx │ │ │ ├── mockProfileModification.tsx │ │ │ ├── mockSearchPostData.tsx │ │ │ ├── mockSearchUserData.tsx │ │ │ └── shared.tsx │ │ ├── @tests │ │ │ ├── useFollow.test.tsx │ │ │ ├── useFollowerList.test.tsx │ │ │ ├── useFollowingList.test.tsx │ │ │ ├── useGithubStatistics.test.tsx │ │ │ ├── useProfile.test.tsx │ │ │ ├── useProfileModificationForm.test.tsx │ │ │ ├── useSearchPostData.test.tsx │ │ │ └── useSearchUserData.test.tsx │ │ ├── common │ │ │ ├── useAuth.ts │ │ │ ├── useAutoAnchor.ts │ │ │ ├── useBottomSlider.ts │ │ │ ├── useDebounce.ts │ │ │ ├── useInfiniteImagePreloader.ts │ │ │ ├── useLocalStorage.ts │ │ │ ├── useModal.ts │ │ │ ├── useScrollPagination.ts │ │ │ ├── useSearchKeyword.ts │ │ │ ├── useSnackbar.ts │ │ │ ├── useStep.ts │ │ │ └── useThrottle.ts │ │ └── service │ │ │ ├── useComments.ts │ │ │ ├── useFeedMutation.ts │ │ │ ├── useFollow.ts │ │ │ ├── useFollowerList.ts │ │ │ ├── useFollowingList.ts │ │ │ ├── useGithubStatistics.ts │ │ │ ├── useGithubTags.ts │ │ │ ├── useHomeFeed.ts │ │ │ ├── usePortfolio.ts │ │ │ ├── usePortfolioContacts.ts │ │ │ ├── usePortfolioIntro.ts │ │ │ ├── usePortfolioProject.ts │ │ │ ├── usePortfolioProjects.ts │ │ │ ├── usePortfolioSection.ts │ │ │ ├── usePortfolioSectionItem.ts │ │ │ ├── usePostAddStep.ts │ │ │ ├── usePostDetail.ts │ │ │ ├── usePostEdit.ts │ │ │ ├── usePostEditStep.ts │ │ │ ├── usePostLikePeople.ts │ │ │ ├── usePostUpload.ts │ │ │ ├── useProfile.ts │ │ │ ├── useProfileModificationForm.ts │ │ │ ├── useSearchPostData.ts │ │ │ ├── useSearchUserData.ts │ │ │ └── useUserFeed.ts │ ├── index.tsx │ ├── mocks │ │ ├── db.json │ │ └── routes.json │ ├── pages │ │ ├── AddPostPage │ │ │ ├── AddPostPage.stories.tsx │ │ │ ├── AddPostPage.style.ts │ │ │ └── AddPostPage.tsx │ │ ├── AuthLoginProcessingPage │ │ │ ├── AuthLoginProcessingPage.stories.tsx │ │ │ ├── AuthLoginProcessingPage.style.ts │ │ │ └── AuthLoginProcessingPage.tsx │ │ ├── CommentsPage │ │ │ ├── CommentsPage.stories.tsx │ │ │ ├── CommentsPage.style.tsx │ │ │ └── CommentsPage.tsx │ │ ├── EditPostPage │ │ │ ├── EditPostPage.stories.tsx │ │ │ ├── EditPostPage.style.ts │ │ │ └── EditPostPage.tsx │ │ ├── FollowerListPage │ │ │ ├── FollowerListPage.style.ts │ │ │ └── FollowerListPage.tsx │ │ ├── FollowingListPage │ │ │ ├── FollowingListPage.style.ts │ │ │ └── FollowingListPage.tsx │ │ ├── HomeFeedPage │ │ │ ├── HomeFeedPage.stories.tsx │ │ │ ├── HomeFeedPage.style.ts │ │ │ └── HomeFeedPage.tsx │ │ ├── LoginPage │ │ │ ├── LoginPage.stories.tsx │ │ │ ├── LoginPage.style.ts │ │ │ └── LoginPage.tsx │ │ ├── PortfolioPage │ │ │ ├── MyPortfolioPage.tsx │ │ │ ├── PortfolioPage.style.ts │ │ │ └── PortfolioPage.tsx │ │ ├── PostLikePeoplePage │ │ │ ├── PostLikePeoplePage.stories.tsx │ │ │ ├── PostLikePeoplePage.style.ts │ │ │ └── PostLikePeoplePage.tsx │ │ ├── ProfilePage │ │ │ ├── ProfilePage.stories.tsx │ │ │ ├── ProfilePage.style.ts │ │ │ └── ProfilePage.tsx │ │ ├── SearchPage │ │ │ ├── SearchPage.stories.tsx │ │ │ ├── SearchPage.style.ts │ │ │ └── SearchPage.tsx │ │ ├── SearchPostResultPage │ │ │ ├── SearchPostResultPage.style.ts │ │ │ └── SearchPostResultPage.tsx │ │ └── UserFeedPage │ │ │ ├── UserFeedPage.stories.tsx │ │ │ ├── UserFeedPage.style.ts │ │ │ └── UserFeedPage.tsx │ ├── services │ │ ├── queries │ │ │ ├── comments.ts │ │ │ ├── github.ts │ │ │ ├── githubStats.ts │ │ │ ├── index.ts │ │ │ ├── portfolio.ts │ │ │ ├── postLikePeople.ts │ │ │ ├── posts.ts │ │ │ ├── profile.ts │ │ │ └── search.ts │ │ └── requests │ │ │ ├── account.ts │ │ │ ├── comments.ts │ │ │ ├── github.ts │ │ │ ├── githubStats.ts │ │ │ ├── index.ts │ │ │ ├── portfolio.ts │ │ │ ├── postLikePeople.ts │ │ │ ├── posts.ts │ │ │ ├── profile.ts │ │ │ └── search.ts │ ├── storage │ │ └── storage.ts │ └── utils │ │ ├── data.ts │ │ ├── date.ts │ │ ├── error.ts │ │ ├── history.ts │ │ ├── infiniteData.ts │ │ ├── kakao.ts │ │ ├── layout.ts │ │ ├── portfolio.ts │ │ ├── postUpload.ts │ │ ├── preloaders.ts │ │ ├── profileModification.ts │ │ ├── tabs.ts │ │ ├── text.tsx │ │ └── typeGuard.ts ├── tsconfig.json ├── webpack.config.ts └── yarn.lock ├── jenkinsfiles ├── backend-dev ├── backend-prod ├── frontend-dev ├── frontend-prod └── s3proxy └── s3-proxy ├── README.md └── s3-proxy ├── .gitignore ├── build.gradle ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── script └── deploy_script.sh ├── settings.gradle └── src ├── main ├── java │ └── com │ │ └── woowacourse │ │ └── s3_proxy │ │ ├── S3ProxyApplication.java │ │ ├── common │ │ └── file_validator │ │ │ ├── FileValidator.java │ │ │ └── ImageFileValidator.java │ │ ├── config │ │ ├── FileValidatorConfiguration.java │ │ ├── StorageConfiguration.java │ │ ├── WebMvcConfiguration.java │ │ └── resolver │ │ │ └── ExtensionValidArgumentResolver.java │ │ ├── exception │ │ ├── ApplicationException.java │ │ ├── ExceptionAdvice.java │ │ ├── format │ │ │ ├── FileExtensionException.java │ │ │ └── HashFailureException.java │ │ └── upload │ │ │ └── UploadFailureException.java │ │ └── web │ │ ├── application │ │ ├── PickGitStorageService.java │ │ └── dto │ │ │ └── FilesDto.java │ │ ├── domain │ │ └── PickGitStorage.java │ │ ├── infrastructure │ │ ├── FileNameGenerator.java │ │ └── S3Storage.java │ │ └── presentation │ │ ├── PickGitStorageController.java │ │ ├── dto │ │ └── Files.java │ │ └── resolver │ │ └── ExtensionValid.java └── resources │ ├── application-test.yml │ ├── application.yml │ ├── logback-access.xml │ ├── logback-spring.xml │ ├── logback │ ├── access-logger.xml │ ├── api-logger.xml │ ├── error-logger.xml │ ├── info-logger.xml │ ├── test │ │ ├── test-access-logger.xml │ │ └── test-basic-logger.xml │ └── warn-logger.xml │ └── s3proxy.pub └── test ├── java └── com │ └── woowacourse │ └── s3_proxy │ ├── S3ProxyApplicationTests.java │ ├── acceptance │ └── S3ProxyAcceptanceTest.java │ ├── common │ ├── FileFactory.java │ └── file_validator │ │ └── ImageFileValidatorTest.java │ ├── config │ └── StorageTestConfiguration.java │ ├── integration │ └── S3StorageIntegrationTest.java │ └── web │ ├── application │ └── PickGitStorageServiceTest.java │ ├── infrastructure │ ├── FileNameGeneratorTest.java │ └── S3StorageTest.java │ └── presentation │ └── PickGitStorageControllerTest.java └── resources ├── testFailData.sh ├── testFailImage1.jpg ├── testFailImage2.jpg ├── testRightImage1.png └── testRightImage2.png /.github/ISSUE_TEMPLATE/bug-template.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug Template 3 | about: 버그를 이슈에 등록한다. 4 | title: "[{fix or hotfix}/{branch_name}] 버그를 해결한다." 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | ## 상세 내용 11 | 12 |
13 | 14 | ## 주의사항 15 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature-template.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature Template 3 | about: 구현할 기능을 이슈에 등록한다. 4 | title: "[feature/{branch_name}] 기능을 구현한다." 5 | labels: 'feature' 6 | assignees: '' 7 | 8 | --- 9 | 10 | ## 구현 기능 11 | 12 |
13 | 14 | ## 작업 내용 15 | 16 |
17 | 18 | ## 주의사항 19 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/refactor-template.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Refactor Template 3 | about: 리팩토링할 기능을 이슈에 등록한다. 4 | title: "[refactor/{branch_name}] 기능을 리팩토링한다." 5 | labels: 'refactor' 6 | assignees: '' 7 | 8 | --- 9 | 10 | ## 작업 내용 11 | 12 |
13 | 14 | ## 주의사항 15 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## 상세 내용 2 | 3 |
4 | 5 | ## 기타 6 | 7 |
8 | 9 | Close #{이슈_번호} 10 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "backend/pick-git/security"] 2 | path = backend/pick-git/security 3 | url = https://github.com/2021-pick-git/security.git 4 | branch = main 5 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Datasource local storage ignored files 5 | /dataSources/ 6 | /dataSources.local.xml 7 | # Editor-based HTTP Client requests 8 | /httpRequests/ 9 | -------------------------------------------------------------------------------- /.idea/2021-pick-git.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /backend/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woowacourse-teams/2021-pick-git/79b2c28bba8b72b30ef50c5aeecb904dcfba0c93/backend/README.md -------------------------------------------------------------------------------- /backend/pick-git/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woowacourse-teams/2021-pick-git/79b2c28bba8b72b30ef50c5aeecb904dcfba0c93/backend/pick-git/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /backend/pick-git/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.3-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /backend/pick-git/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'pick-git' 2 | -------------------------------------------------------------------------------- /backend/pick-git/src/docs/asciidoc/authorization.adoc: -------------------------------------------------------------------------------- 1 | ifndef::snippets[] 2 | :snippets: ../../../build/generated-snippets 3 | endif::[] 4 | :doctype: book 5 | :icons: font 6 | :source-highlighter: highlightjs 7 | :toc: left 8 | :toclevels: 4 9 | 10 | == Authorization 11 | === 깃허브 로그인 url 받기 12 | ==== Request 13 | include::{snippets}/authorization - githubLogin/http-request.adoc[] 14 | ==== Response 15 | include::{snippets}/authorization - githubLogin/http-response.adoc[] 16 | 17 | === 깃허브 로그인 후 18 | ==== Request 19 | include::{snippets}/authorization - afterlogin/http-request.adoc[] 20 | ==== Response 21 | include::{snippets}/authorization - afterlogin/http-response.adoc[] -------------------------------------------------------------------------------- /backend/pick-git/src/docs/asciidoc/index.adoc: -------------------------------------------------------------------------------- 1 | ifndef::snippets[] 2 | :snippets: ../../../build/generated-snippets 3 | endif::[] 4 | :doctype: book 5 | :icons: font 6 | :source-highlighter: highlightjs 7 | :toc: left 8 | :toclevels: 4 9 | 10 | include::authorization.adoc[] 11 | include::comment.adoc[] 12 | include::following.adoc[] 13 | include::post.adoc[] 14 | include::postfeed.adoc[] 15 | include::profile.adoc[] 16 | include::tag.adoc[] 17 | include::search.adoc[] 18 | include::portfolio.adoc[] 19 | 20 | PickGit API 21 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/java/com/woowacourse/pickgit/PickGitApplication.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class PickGitApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(PickGitApplication.class, args); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/java/com/woowacourse/pickgit/authentication/application/JwtTokenProvider.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.authentication.application; 2 | 3 | public interface JwtTokenProvider { 4 | 5 | String createToken(String payload); 6 | 7 | boolean validateToken(String token); 8 | 9 | String getPayloadByKey(String token, String key); 10 | } 11 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/java/com/woowacourse/pickgit/authentication/application/dto/TokenDto.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.authentication.application.dto; 2 | 3 | public class TokenDto { 4 | 5 | private String token; 6 | private String username; 7 | 8 | private TokenDto() { 9 | } 10 | 11 | public TokenDto(String token, String username) { 12 | this.token = token; 13 | this.username = username; 14 | } 15 | 16 | public String getToken() { 17 | return token; 18 | } 19 | 20 | public String getUsername() { 21 | return username; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/java/com/woowacourse/pickgit/authentication/domain/Authenticated.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.authentication.domain; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | @Target(ElementType.PARAMETER) 9 | @Retention(RetentionPolicy.RUNTIME) 10 | public @interface Authenticated { 11 | } 12 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/java/com/woowacourse/pickgit/authentication/domain/OAuthAccessTokenDao.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.authentication.domain; 2 | 3 | import java.util.Optional; 4 | 5 | public interface OAuthAccessTokenDao { 6 | 7 | void insert(String token, String oauthAccessToken); 8 | 9 | Optional findByKeyToken(String token); 10 | } 11 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/java/com/woowacourse/pickgit/authentication/domain/OAuthClient.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.authentication.domain; 2 | 3 | import com.woowacourse.pickgit.authentication.application.dto.OAuthProfileResponse; 4 | 5 | public interface OAuthClient { 6 | 7 | String getLoginUrl(); 8 | 9 | String getAccessToken(String code); 10 | 11 | OAuthProfileResponse getGithubProfile(String githubAccessToken); 12 | } 13 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/java/com/woowacourse/pickgit/authentication/domain/user/AppUser.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.authentication.domain.user; 2 | 3 | public abstract class AppUser { 4 | 5 | private final String username; 6 | private final String accessToken; 7 | 8 | protected AppUser(String username, String accessToken) { 9 | this.username = username; 10 | this.accessToken = accessToken; 11 | } 12 | 13 | public String getUsername() { 14 | return username; 15 | } 16 | 17 | public String getAccessToken() { 18 | return accessToken; 19 | } 20 | 21 | public abstract boolean isGuest(); 22 | } 23 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/java/com/woowacourse/pickgit/authentication/domain/user/LoginUser.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.authentication.domain.user; 2 | 3 | public class LoginUser extends AppUser { 4 | 5 | public LoginUser(String username, String accessToken) { 6 | super(username, accessToken); 7 | } 8 | 9 | @Override 10 | public boolean isGuest() { 11 | return false; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/java/com/woowacourse/pickgit/authentication/presentation/dto/OAuthLoginUrlResponse.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.authentication.presentation.dto; 2 | 3 | public class OAuthLoginUrlResponse { 4 | 5 | private String url; 6 | 7 | private OAuthLoginUrlResponse() { 8 | } 9 | 10 | public OAuthLoginUrlResponse(String url) { 11 | this.url = url; 12 | } 13 | 14 | public String getUrl() { 15 | return url; 16 | } 17 | 18 | public void setUrl(String url) { 19 | this.url = url; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/java/com/woowacourse/pickgit/authentication/presentation/dto/OAuthTokenResponse.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.authentication.presentation.dto; 2 | 3 | public class OAuthTokenResponse { 4 | 5 | private String token; 6 | private String username; 7 | 8 | private OAuthTokenResponse() { 9 | } 10 | 11 | public OAuthTokenResponse(String token, String username) { 12 | this.token = token; 13 | this.username = username; 14 | } 15 | 16 | public String getToken() { 17 | return token; 18 | } 19 | 20 | public String getUsername() { 21 | return username; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/java/com/woowacourse/pickgit/authentication/presentation/interceptor/AuthHeader.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.authentication.presentation.interceptor; 2 | 3 | public interface AuthHeader { 4 | 5 | String AUTHENTICATION = "Authentication"; 6 | } 7 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/java/com/woowacourse/pickgit/comment/domain/CommentRepository.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.comment.domain; 2 | 3 | import java.util.List; 4 | import org.springframework.data.domain.Pageable; 5 | import org.springframework.data.jpa.repository.JpaRepository; 6 | 7 | public interface CommentRepository extends JpaRepository { 8 | 9 | List findCommentsByPost_Id(Long postId, Pageable pageable); 10 | } 11 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/java/com/woowacourse/pickgit/comment/domain/Comments.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.comment.domain; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import javax.persistence.CascadeType; 6 | import javax.persistence.Embeddable; 7 | import javax.persistence.FetchType; 8 | import javax.persistence.OneToMany; 9 | 10 | @Embeddable 11 | public class Comments { 12 | 13 | @OneToMany(mappedBy = "post", fetch = FetchType.LAZY, cascade = CascadeType.REMOVE) 14 | private List comments; 15 | 16 | public Comments() { 17 | this(new ArrayList<>()); 18 | } 19 | 20 | public Comments(List comments) { 21 | this.comments = comments; 22 | } 23 | 24 | public List getComments() { 25 | return comments; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/java/com/woowacourse/pickgit/comment/infrastructure/empty: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woowacourse-teams/2021-pick-git/79b2c28bba8b72b30ef50c5aeecb904dcfba0c93/backend/pick-git/src/main/java/com/woowacourse/pickgit/comment/infrastructure/empty -------------------------------------------------------------------------------- /backend/pick-git/src/main/java/com/woowacourse/pickgit/comment/presentation/dto/request/ContentRequest.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.comment.presentation.dto.request; 2 | 3 | import javax.validation.constraints.NotBlank; 4 | import javax.validation.constraints.Size; 5 | 6 | public class ContentRequest { 7 | 8 | @NotBlank(message = "F0001") 9 | @Size(max = 100, message = "F0002") 10 | private String content; 11 | 12 | private ContentRequest() { 13 | } 14 | 15 | public ContentRequest(String content) { 16 | this.content = content; 17 | } 18 | 19 | public String getContent() { 20 | return content; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/java/com/woowacourse/pickgit/config/WebClientConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.config; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.web.reactive.function.client.WebClient; 6 | 7 | @Configuration 8 | public class WebClientConfiguration { 9 | 10 | @Bean 11 | public WebClient webClient() { 12 | return WebClient.create(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/java/com/woowacourse/pickgit/config/auth_interceptor_register/ForLoginAndGuestUser.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.config.auth_interceptor_register; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | @Retention(RetentionPolicy.RUNTIME) 9 | @Target(ElementType.METHOD) 10 | public @interface ForLoginAndGuestUser { 11 | } 12 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/java/com/woowacourse/pickgit/config/auth_interceptor_register/ForOnlyLoginUser.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.config.auth_interceptor_register; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | @Retention(RetentionPolicy.RUNTIME) 9 | @Target(ElementType.METHOD) 10 | public @interface ForOnlyLoginUser { 11 | } 12 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/java/com/woowacourse/pickgit/config/auth_interceptor_register/register_type/RegisterType.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.config.auth_interceptor_register.register_type; 2 | 3 | public enum RegisterType { 4 | AUTHENTICATE, 5 | IGNORE_AUTHENTICATE 6 | } 7 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/java/com/woowacourse/pickgit/config/auth_interceptor_register/register_type/StorageForRegisterType.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.config.auth_interceptor_register.register_type; 2 | 3 | import com.woowacourse.pickgit.authentication.presentation.interceptor.PathMatchInterceptor; 4 | import org.springframework.http.HttpMethod; 5 | 6 | public interface StorageForRegisterType { 7 | 8 | void appendTo(PathMatchInterceptor include); 9 | boolean isSatisfiedBy(RegisterType registerType); 10 | void put(String key, HttpMethod value); 11 | RegisterType getType(); 12 | } 13 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/java/com/woowacourse/pickgit/config/auth_interceptor_register/scanner/ForGuestScanner.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.config.auth_interceptor_register.scanner; 2 | 3 | import static java.util.stream.Collectors.toList; 4 | 5 | import com.woowacourse.pickgit.config.auth_interceptor_register.ForLoginAndGuestUser; 6 | import java.lang.reflect.Method; 7 | import java.util.Arrays; 8 | import java.util.List; 9 | 10 | public class ForGuestScanner { 11 | 12 | public List parseMethods(Class controller) { 13 | return Arrays.stream(controller.getMethods()) 14 | .filter(method -> method.isAnnotationPresent(ForLoginAndGuestUser.class)) 15 | .collect(toList()); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/java/com/woowacourse/pickgit/config/auth_interceptor_register/scanner/ForLoginUserScanner.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.config.auth_interceptor_register.scanner; 2 | 3 | import static java.util.stream.Collectors.toList; 4 | 5 | import com.woowacourse.pickgit.config.auth_interceptor_register.ForOnlyLoginUser; 6 | import java.lang.reflect.Method; 7 | import java.util.Arrays; 8 | import java.util.List; 9 | 10 | public class ForLoginUserScanner { 11 | 12 | public List parseMethods(Class controller) { 13 | return Arrays.stream(controller.getMethods()) 14 | .filter(method -> method.isAnnotationPresent(ForOnlyLoginUser.class)) 15 | .collect(toList()); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/java/com/woowacourse/pickgit/config/database/SlaveNames.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.config.database; 2 | 3 | import java.util.List; 4 | 5 | public class SlaveNames { 6 | 7 | private final String[] value; 8 | private int counter = 0; 9 | 10 | public SlaveNames(List slaveDataSourceProperties) { 11 | this(slaveDataSourceProperties.toArray(String[]::new)); 12 | } 13 | 14 | public SlaveNames(String[] value) { 15 | this.value = value; 16 | } 17 | 18 | public String getNextName() { 19 | int index = counter; 20 | counter = (counter + 1) % value.length; 21 | return value[index]; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/java/com/woowacourse/pickgit/exception/ApplicationException.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.exception; 2 | 3 | import org.springframework.http.HttpStatus; 4 | 5 | public abstract class ApplicationException extends RuntimeException { 6 | 7 | private final String errorCode; 8 | private final HttpStatus httpStatus; 9 | 10 | protected ApplicationException(String errorCode, HttpStatus httpStatus, String message) { 11 | super(message); 12 | this.errorCode = errorCode; 13 | this.httpStatus = httpStatus; 14 | } 15 | 16 | public String getErrorCode() { 17 | return errorCode; 18 | } 19 | 20 | public HttpStatus getHttpStatus() { 21 | return httpStatus; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/java/com/woowacourse/pickgit/exception/authentication/AuthenticationException.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.exception.authentication; 2 | 3 | import com.woowacourse.pickgit.exception.ApplicationException; 4 | import org.springframework.http.HttpStatus; 5 | 6 | public abstract class AuthenticationException extends ApplicationException { 7 | 8 | protected AuthenticationException(String errorCode, HttpStatus httpStatus, String message) { 9 | super(errorCode, httpStatus, message); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/java/com/woowacourse/pickgit/exception/authentication/InvalidTokenException.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.exception.authentication; 2 | 3 | import org.springframework.http.HttpStatus; 4 | 5 | public class InvalidTokenException extends AuthenticationException { 6 | 7 | private static final String CODE = "A0001"; 8 | private static final String MESSAGE = "토큰 인증 에러"; 9 | 10 | public InvalidTokenException() { 11 | super(CODE, HttpStatus.UNAUTHORIZED, MESSAGE); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/java/com/woowacourse/pickgit/exception/authentication/UnauthorizedException.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.exception.authentication; 2 | 3 | import org.springframework.http.HttpStatus; 4 | 5 | public class UnauthorizedException extends AuthenticationException { 6 | 7 | private static final String CODE = "A0002"; 8 | private static final String MESSAGE = "권한 에러"; 9 | 10 | public UnauthorizedException() { 11 | super(CODE, HttpStatus.UNAUTHORIZED, MESSAGE); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/java/com/woowacourse/pickgit/exception/comment/CannotDeleteCommentException.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.exception.comment; 2 | 3 | import org.springframework.http.HttpStatus; 4 | 5 | public class CannotDeleteCommentException extends CommentException { 6 | 7 | private static final String CODE = "P0007"; 8 | private static final String MESSAGE = "남 게시물, 남 댓글은 삭제할 수 없습니다."; 9 | 10 | public CannotDeleteCommentException() { 11 | super(CODE, HttpStatus.UNAUTHORIZED, MESSAGE); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/java/com/woowacourse/pickgit/exception/comment/CommentException.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.exception.comment; 2 | 3 | import com.woowacourse.pickgit.exception.ApplicationException; 4 | import org.springframework.http.HttpStatus; 5 | 6 | public abstract class CommentException extends ApplicationException { 7 | 8 | protected CommentException(String errorCode, HttpStatus httpStatus, String message) { 9 | super(errorCode, httpStatus, message); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/java/com/woowacourse/pickgit/exception/comment/CommentNotFoundException.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.exception.comment; 2 | 3 | import org.springframework.http.HttpStatus; 4 | 5 | public class CommentNotFoundException extends CommentException { 6 | 7 | private static final String CODE = "P0008"; 8 | private static final String MESSAGE = "해당 댓글이 존재하지 않습니다."; 9 | 10 | public CommentNotFoundException() { 11 | super(CODE, HttpStatus.BAD_REQUEST, MESSAGE); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/java/com/woowacourse/pickgit/exception/dto/ApiErrorResponse.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.exception.dto; 2 | 3 | public class ApiErrorResponse { 4 | 5 | private String errorCode; 6 | 7 | private ApiErrorResponse() { 8 | } 9 | 10 | public ApiErrorResponse(String errorCode) { 11 | this.errorCode = errorCode; 12 | } 13 | 14 | public String getErrorCode() { 15 | return errorCode; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/java/com/woowacourse/pickgit/exception/platform/PlatformException.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.exception.platform; 2 | 3 | import com.woowacourse.pickgit.exception.ApplicationException; 4 | import org.springframework.http.HttpStatus; 5 | 6 | public abstract class PlatformException extends ApplicationException { 7 | 8 | protected PlatformException(String errorCode, HttpStatus httpStatus, String message) { 9 | super(errorCode, httpStatus, message); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/java/com/woowacourse/pickgit/exception/platform/PlatformHttpErrorException.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.exception.platform; 2 | 3 | import org.springframework.http.HttpStatus; 4 | 5 | public class PlatformHttpErrorException extends PlatformException { 6 | 7 | private static final String CODE = "V0001"; 8 | private static final String MESSAGE = "외부 플랫폼 연동에 실패"; 9 | 10 | public PlatformHttpErrorException() { 11 | this(MESSAGE); 12 | } 13 | 14 | public PlatformHttpErrorException(String message) { 15 | super(CODE, HttpStatus.INTERNAL_SERVER_ERROR, message); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/java/com/woowacourse/pickgit/exception/platform/PlatformInternalThreadException.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.exception.platform; 2 | 3 | import org.springframework.http.HttpStatus; 4 | 5 | public class PlatformInternalThreadException extends PlatformException{ 6 | 7 | private static final String CODE = "V0003"; 8 | private static final String MESSAGE = "외부 플랫폼 데이터 추출중 내부 스레드 오류"; 9 | 10 | public PlatformInternalThreadException() { 11 | this(MESSAGE); 12 | } 13 | 14 | private PlatformInternalThreadException(String message) { 15 | super(CODE, HttpStatus.INTERNAL_SERVER_ERROR, message); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/java/com/woowacourse/pickgit/exception/portfolio/NoSuchPortfolioException.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.exception.portfolio; 2 | 3 | import org.springframework.http.HttpStatus; 4 | 5 | public class NoSuchPortfolioException extends PortfolioException { 6 | 7 | private static final String ERROR_CODE = "R0001"; 8 | private static final HttpStatus HTTP_STATUS = HttpStatus.BAD_REQUEST; 9 | private static final String MESSAGE = "존재하지 않는 포트폴리오 조회 에러"; 10 | 11 | public NoSuchPortfolioException() { 12 | this(ERROR_CODE, HTTP_STATUS, MESSAGE); 13 | } 14 | 15 | private NoSuchPortfolioException(String errorCode, HttpStatus httpStatus, String message) { 16 | super(errorCode, httpStatus, message); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/java/com/woowacourse/pickgit/exception/portfolio/NotYetCreatedPortfolioException.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.exception.portfolio; 2 | 3 | import org.springframework.http.HttpStatus; 4 | 5 | public class NotYetCreatedPortfolioException extends PortfolioException { 6 | 7 | private static final String ERROR_CODE = "R0006"; 8 | private static final HttpStatus HTTP_STATUS = HttpStatus.NO_CONTENT; 9 | private static final String MESSAGE = "아직 생성되지 않은 포트폴리오 조회"; 10 | 11 | public NotYetCreatedPortfolioException() { 12 | this(ERROR_CODE, HTTP_STATUS, MESSAGE); 13 | } 14 | 15 | private NotYetCreatedPortfolioException(String errorCode, HttpStatus httpStatus, 16 | String message) { 17 | super(errorCode, httpStatus, message); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/java/com/woowacourse/pickgit/exception/portfolio/PortfolioConstraintException.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.exception.portfolio; 2 | 3 | import org.springframework.http.HttpStatus; 4 | 5 | public class PortfolioConstraintException extends PortfolioException { 6 | 7 | private static final String ERROR_CODE = "R0006"; 8 | private static final HttpStatus HTTP_STATUS = HttpStatus.BAD_REQUEST; 9 | 10 | public PortfolioConstraintException(String message) { 11 | this(ERROR_CODE, HTTP_STATUS, message); 12 | } 13 | 14 | protected PortfolioConstraintException( 15 | String errorCode, 16 | HttpStatus httpStatus, 17 | String message 18 | ) { 19 | super(errorCode, httpStatus, message); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/java/com/woowacourse/pickgit/exception/portfolio/PortfolioException.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.exception.portfolio; 2 | 3 | import com.woowacourse.pickgit.exception.ApplicationException; 4 | import org.springframework.http.HttpStatus; 5 | 6 | public abstract class PortfolioException extends ApplicationException { 7 | 8 | protected PortfolioException( 9 | String errorCode, 10 | HttpStatus httpStatus, 11 | String message 12 | ) { 13 | super(errorCode, httpStatus, message); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/java/com/woowacourse/pickgit/exception/portfolio/ProjectTypeNotFoundException.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.exception.portfolio; 2 | 3 | import org.springframework.http.HttpStatus; 4 | 5 | public class ProjectTypeNotFoundException extends PortfolioException { 6 | 7 | private static final String ERROR_CODE = "R0002"; 8 | private static final HttpStatus HTTP_STATUS = HttpStatus.NOT_FOUND; 9 | private static final String MESSAGE = "프로젝트 타입 찾을 수 없음 에러"; 10 | 11 | public ProjectTypeNotFoundException() { 12 | this(ERROR_CODE, HTTP_STATUS, MESSAGE); 13 | } 14 | 15 | private ProjectTypeNotFoundException(String errorCode, HttpStatus httpStatus, String message) { 16 | super(errorCode, httpStatus, message); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/java/com/woowacourse/pickgit/exception/post/CannotAddTagException.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.exception.post; 2 | 3 | import org.springframework.http.HttpStatus; 4 | 5 | public class CannotAddTagException extends PostException { 6 | 7 | private static final String CODE = "P0001"; 8 | private static final String MESSAGE = "태그 추가 에러"; 9 | 10 | public CannotAddTagException() { 11 | super(CODE, HttpStatus.BAD_REQUEST, MESSAGE); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/java/com/woowacourse/pickgit/exception/post/CannotUnlikeException.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.exception.post; 2 | 3 | import com.woowacourse.pickgit.exception.ApplicationException; 4 | import org.springframework.http.HttpStatus; 5 | 6 | public class CannotUnlikeException extends ApplicationException { 7 | 8 | private static final String CODE = "P0004"; 9 | private static final String MESSAGE = "좋아요 하지 않은 게시물 좋아요 취소 에러"; 10 | 11 | public CannotUnlikeException() { 12 | super(CODE, HttpStatus.BAD_REQUEST, MESSAGE); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/java/com/woowacourse/pickgit/exception/post/CommentFormatException.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.exception.post; 2 | 3 | import org.springframework.http.HttpStatus; 4 | 5 | public class CommentFormatException extends PostException { 6 | 7 | private static final String CODE = "F0002"; 8 | private static final String MESSAGE = "댓글 길이 에러"; 9 | 10 | public CommentFormatException() { 11 | super(CODE, HttpStatus.BAD_REQUEST, MESSAGE); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/java/com/woowacourse/pickgit/exception/post/DuplicatedLikeException.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.exception.post; 2 | 3 | import com.woowacourse.pickgit.exception.ApplicationException; 4 | import org.springframework.http.HttpStatus; 5 | 6 | public class DuplicatedLikeException extends ApplicationException { 7 | 8 | private static final String CODE = "P0003"; 9 | private static final String MESSAGE = "이미 좋아요한 게시물 중복 좋아요 에러"; 10 | 11 | public DuplicatedLikeException() { 12 | super(CODE, HttpStatus.BAD_REQUEST, MESSAGE); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/java/com/woowacourse/pickgit/exception/post/HomeFeedTypeException.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.exception.post; 2 | 3 | import org.springframework.http.HttpStatus; 4 | 5 | public class HomeFeedTypeException extends PostException { 6 | 7 | private static final String ERROR_CODE = "P0010"; 8 | private static final HttpStatus HTTP_STATUS = HttpStatus.INTERNAL_SERVER_ERROR; 9 | private static final String MESSAGE = "홈피드 타입을 찾을 수 없습니다."; 10 | 11 | public HomeFeedTypeException() { 12 | this(ERROR_CODE, HTTP_STATUS, MESSAGE); 13 | } 14 | 15 | private HomeFeedTypeException(String errorCode, HttpStatus httpStatus, String message) { 16 | super(errorCode, httpStatus, message); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/java/com/woowacourse/pickgit/exception/post/IllegalSearchTypeException.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.exception.post; 2 | 3 | import org.springframework.http.HttpStatus; 4 | 5 | public class IllegalSearchTypeException extends PostException { 6 | 7 | private static final String ERROR_CODE = "P0006"; 8 | private static final HttpStatus HTTP_STATUS = HttpStatus.BAD_REQUEST; 9 | private static final String MESSAGE = "검색 타입이 존재하지 않습니다."; 10 | 11 | public IllegalSearchTypeException() { 12 | this(ERROR_CODE, HTTP_STATUS, MESSAGE); 13 | } 14 | 15 | public IllegalSearchTypeException( 16 | String errorCode, 17 | HttpStatus httpStatus, 18 | String message 19 | ) { 20 | super(errorCode, httpStatus, message); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/java/com/woowacourse/pickgit/exception/post/PostException.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.exception.post; 2 | 3 | import com.woowacourse.pickgit.exception.ApplicationException; 4 | import org.springframework.http.HttpStatus; 5 | 6 | public abstract class PostException extends ApplicationException { 7 | 8 | protected PostException(String errorCode, HttpStatus httpStatus, String message) { 9 | super(errorCode, httpStatus, message); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/java/com/woowacourse/pickgit/exception/post/PostFormatException.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.exception.post; 2 | 3 | import org.springframework.http.HttpStatus; 4 | 5 | public class PostFormatException extends PostException { 6 | 7 | private static final String CODE = "F0004"; 8 | private static final String MESSAGE = "게시물 길이 에러"; 9 | 10 | public PostFormatException() { 11 | super(CODE, HttpStatus.BAD_REQUEST, MESSAGE); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/java/com/woowacourse/pickgit/exception/post/PostNotBelongToUserException.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.exception.post; 2 | 3 | import org.springframework.http.HttpStatus; 4 | 5 | public class PostNotBelongToUserException extends PostException{ 6 | 7 | private static final String CODE = "P0005"; 8 | private static final String MESSAGE = "해당하는 사용자의 게시물이 아닙니다."; 9 | 10 | public PostNotBelongToUserException() { 11 | super(CODE, HttpStatus.UNAUTHORIZED, MESSAGE); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/java/com/woowacourse/pickgit/exception/post/PostNotFoundException.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.exception.post; 2 | 3 | import org.springframework.http.HttpStatus; 4 | 5 | public class PostNotFoundException extends PostException { 6 | 7 | private static final String ERROR_CODE = "P0002"; 8 | private static final HttpStatus HTTP_STATUS = HttpStatus.INTERNAL_SERVER_ERROR; 9 | private static final String MESSAGE = "해당하는 게시물을 찾을 수 없습니다."; 10 | 11 | public PostNotFoundException() { 12 | this(ERROR_CODE, HTTP_STATUS, MESSAGE); 13 | } 14 | 15 | public PostNotFoundException( 16 | String errorCode, 17 | HttpStatus httpStatus, 18 | String message 19 | ) { 20 | super(errorCode, httpStatus, message); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/java/com/woowacourse/pickgit/exception/post/RepositoryParseException.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.exception.post; 2 | 3 | import org.springframework.http.HttpStatus; 4 | 5 | public class RepositoryParseException extends PostException { 6 | 7 | private static final String CODE = "V0001"; 8 | private static final String MESSAGE = "레포지토리 목록을 불러올 수 없습니다."; 9 | 10 | public RepositoryParseException() { 11 | super(CODE, HttpStatus.INTERNAL_SERVER_ERROR, MESSAGE); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/java/com/woowacourse/pickgit/exception/post/TagFormatException.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.exception.post; 2 | 3 | import org.springframework.http.HttpStatus; 4 | 5 | public class TagFormatException extends PostException { 6 | 7 | private static final String CODE = "F0003"; 8 | private static final String MESSAGE = "태그 포맷 에러"; 9 | 10 | public TagFormatException() { 11 | super(CODE, HttpStatus.BAD_REQUEST,MESSAGE); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/java/com/woowacourse/pickgit/exception/user/ContributionParseException.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.exception.user; 2 | 3 | import org.springframework.http.HttpStatus; 4 | 5 | public class ContributionParseException extends UserException { 6 | 7 | private static final String CODE = "V0001"; 8 | private static final String MESSAGE = "활동 통계를 조회할 수 없습니다."; 9 | 10 | public ContributionParseException() { 11 | super(CODE, HttpStatus.INTERNAL_SERVER_ERROR, MESSAGE); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/java/com/woowacourse/pickgit/exception/user/DuplicateFollowException.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.exception.user; 2 | 3 | import org.springframework.http.HttpStatus; 4 | 5 | public class DuplicateFollowException extends UserException { 6 | 7 | private static final String CODE = "U0002"; 8 | private static final String MESSAGE = "이미 팔로우 중 입니다."; 9 | 10 | public DuplicateFollowException() { 11 | super(CODE, HttpStatus.BAD_REQUEST, MESSAGE); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/java/com/woowacourse/pickgit/exception/user/InvalidFollowException.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.exception.user; 2 | 3 | import org.springframework.http.HttpStatus; 4 | 5 | public class InvalidFollowException extends UserException { 6 | 7 | private static final String CODE = "U0003"; 8 | private static final String MESSAGE = "존재하지 않는 팔로우 입니다."; 9 | 10 | public InvalidFollowException() { 11 | super(CODE, HttpStatus.BAD_REQUEST, MESSAGE); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/java/com/woowacourse/pickgit/exception/user/InvalidUserException.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.exception.user; 2 | 3 | import org.springframework.http.HttpStatus; 4 | 5 | public class InvalidUserException extends UserException { 6 | 7 | private static final String CODE = "U0001"; 8 | private static final String MESSAGE = "유효하지 않은 유저입니다."; 9 | 10 | public InvalidUserException() { 11 | super(CODE, HttpStatus.BAD_REQUEST, MESSAGE); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/java/com/woowacourse/pickgit/exception/user/SameSourceTargetUserException.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.exception.user; 2 | 3 | import org.springframework.http.HttpStatus; 4 | 5 | public class SameSourceTargetUserException extends UserException { 6 | 7 | private static final String CODE = "U0004"; 8 | private static final String MESSAGE = "같은 Source 와 Target 유저입니다."; 9 | 10 | public SameSourceTargetUserException() { 11 | super(CODE, HttpStatus.BAD_REQUEST, MESSAGE); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/java/com/woowacourse/pickgit/exception/user/UserException.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.exception.user; 2 | 3 | import com.woowacourse.pickgit.exception.ApplicationException; 4 | import org.springframework.http.HttpStatus; 5 | 6 | public abstract class UserException extends ApplicationException { 7 | 8 | protected UserException(String errorCode, HttpStatus httpStatus, String message) { 9 | super(errorCode, httpStatus, message); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/java/com/woowacourse/pickgit/exception/user/UserNotFoundException.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.exception.user; 2 | 3 | import org.springframework.http.HttpStatus; 4 | 5 | public class UserNotFoundException extends UserException { 6 | 7 | private static final String ERROR_CODE = "U0001"; 8 | private static final HttpStatus HTTP_STATUS = HttpStatus.INTERNAL_SERVER_ERROR; 9 | private static final String MESSAGE = "해당하는 사용자를 찾을 수 없습니다."; 10 | 11 | public UserNotFoundException() { 12 | this(ERROR_CODE, HTTP_STATUS, MESSAGE); 13 | } 14 | 15 | public UserNotFoundException(String errorCode, HttpStatus httpStatus, String message) { 16 | super(errorCode, httpStatus, message); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/java/com/woowacourse/pickgit/portfolio/application/dto/request/TagRequestDto.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.portfolio.application.dto.request; 2 | 3 | import com.woowacourse.pickgit.portfolio.presentation.dto.request.TagRequest; 4 | import lombok.Builder; 5 | 6 | @Builder 7 | public class TagRequestDto { 8 | 9 | private String name; 10 | 11 | private TagRequestDto() { 12 | } 13 | 14 | public TagRequestDto(String name) { 15 | this.name = name; 16 | } 17 | 18 | public static TagRequestDto of(TagRequest request) { 19 | return TagRequestDto.builder() 20 | .name(request.getName()) 21 | .build(); 22 | } 23 | 24 | public String getName() { 25 | return name; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/java/com/woowacourse/pickgit/portfolio/domain/common/Updatable.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.portfolio.domain.common; 2 | 3 | public interface Updatable { 4 | 5 | void update(T t); 6 | 7 | boolean semanticallyEquals(Object o); 8 | 9 | int semanticallyHashcode(); 10 | } 11 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/java/com/woowacourse/pickgit/portfolio/domain/repository/PortfolioRepository.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.portfolio.domain.repository; 2 | 3 | import com.woowacourse.pickgit.portfolio.domain.Portfolio; 4 | import java.util.Optional; 5 | import org.springframework.data.jpa.repository.JpaRepository; 6 | import org.springframework.data.jpa.repository.Query; 7 | import org.springframework.data.repository.query.Param; 8 | 9 | public interface PortfolioRepository extends JpaRepository { 10 | 11 | @Query("select p from Portfolio p where p.user.basicProfile.name = :username") 12 | Optional findPortfolioByUsername(@Param("username") String username); 13 | } 14 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/java/com/woowacourse/pickgit/portfolio/infrastructure/empty.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woowacourse-teams/2021-pick-git/79b2c28bba8b72b30ef50c5aeecb904dcfba0c93/backend/pick-git/src/main/java/com/woowacourse/pickgit/portfolio/infrastructure/empty.txt -------------------------------------------------------------------------------- /backend/pick-git/src/main/java/com/woowacourse/pickgit/portfolio/presentation/dto/request/ContactRequest.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.portfolio.presentation.dto.request; 2 | 3 | import lombok.Builder; 4 | 5 | @Builder 6 | public class ContactRequest { 7 | 8 | private Long id; 9 | private String category; 10 | private String value; 11 | 12 | private ContactRequest() { 13 | } 14 | 15 | public ContactRequest(Long id, String category, String value) { 16 | this.id = id; 17 | this.category = category; 18 | this.value = value; 19 | } 20 | 21 | public Long getId() { 22 | return id; 23 | } 24 | 25 | public String getCategory() { 26 | return category; 27 | } 28 | 29 | public String getValue() { 30 | return value; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/java/com/woowacourse/pickgit/portfolio/presentation/dto/request/DescriptionRequest.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.portfolio.presentation.dto.request; 2 | 3 | import lombok.Builder; 4 | 5 | @Builder 6 | public class DescriptionRequest { 7 | 8 | private Long id; 9 | private String value; 10 | 11 | private DescriptionRequest() { 12 | } 13 | 14 | public DescriptionRequest(Long id, String value) { 15 | this.id = id; 16 | this.value = value; 17 | } 18 | 19 | public Long getId() { 20 | return id; 21 | } 22 | 23 | public String getValue() { 24 | return value; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/java/com/woowacourse/pickgit/portfolio/presentation/dto/request/TagRequest.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.portfolio.presentation.dto.request; 2 | 3 | import lombok.Builder; 4 | 5 | @Builder 6 | public class TagRequest { 7 | 8 | private String name; 9 | 10 | private TagRequest() { 11 | } 12 | 13 | public TagRequest(String name) { 14 | this.name = name; 15 | } 16 | 17 | public String getName() { 18 | return name; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/java/com/woowacourse/pickgit/portfolio/presentation/dto/response/DescriptionResponse.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.portfolio.presentation.dto.response; 2 | 3 | import lombok.Builder; 4 | 5 | @Builder 6 | public class DescriptionResponse { 7 | 8 | private Long id; 9 | private String value; 10 | 11 | private DescriptionResponse() { 12 | } 13 | 14 | public DescriptionResponse(Long id, String value) { 15 | this.id = id; 16 | this.value = value; 17 | } 18 | 19 | public Long getId() { 20 | return id; 21 | } 22 | 23 | public String getValue() { 24 | return value; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/java/com/woowacourse/pickgit/post/application/dto/response/LikeResponseDto.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.post.application.dto.response; 2 | 3 | public class LikeResponseDto { 4 | 5 | private int likesCount; 6 | private Boolean liked; 7 | 8 | private LikeResponseDto() { 9 | } 10 | 11 | public LikeResponseDto(int likesCount, boolean liked) { 12 | this.likesCount = likesCount; 13 | this.liked = liked; 14 | } 15 | 16 | public int getLikesCount() { 17 | return likesCount; 18 | } 19 | 20 | public Boolean getLiked() { 21 | return liked; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/java/com/woowacourse/pickgit/post/application/dto/response/PostUpdateResponseDto.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.post.application.dto.response; 2 | 3 | import java.util.List; 4 | import lombok.Builder; 5 | 6 | @Builder 7 | public class PostUpdateResponseDto { 8 | 9 | private List tags; 10 | private String content; 11 | 12 | private PostUpdateResponseDto() { 13 | } 14 | 15 | public PostUpdateResponseDto(List tags, String content) { 16 | this.tags = tags; 17 | this.content = content; 18 | } 19 | 20 | public List getTags() { 21 | return tags; 22 | } 23 | 24 | public String getContent() { 25 | return content; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/java/com/woowacourse/pickgit/post/application/dto/response/RepositoryResponseDto.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.post.application.dto.response; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import lombok.Builder; 5 | 6 | @Builder 7 | public class RepositoryResponseDto { 8 | 9 | private String name; 10 | 11 | @JsonProperty("html_url") 12 | private String url; 13 | 14 | private RepositoryResponseDto() { 15 | } 16 | 17 | public RepositoryResponseDto(String name, String url) { 18 | this.name = name; 19 | this.url = url; 20 | } 21 | 22 | public String getName() { 23 | return name; 24 | } 25 | 26 | public String getUrl() { 27 | return url; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/java/com/woowacourse/pickgit/post/application/dto/response/RepositoryResponsesDto.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.post.application.dto.response; 2 | 3 | import java.util.List; 4 | import lombok.Builder; 5 | 6 | @Builder 7 | public class RepositoryResponsesDto { 8 | 9 | 10 | private List repositoryResponsesDto; 11 | 12 | private RepositoryResponsesDto() { 13 | } 14 | 15 | public RepositoryResponsesDto(List repositoryResponsesDto) { 16 | this.repositoryResponsesDto = repositoryResponsesDto; 17 | } 18 | 19 | public List getRepositoryResponsesDto() { 20 | return repositoryResponsesDto; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/java/com/woowacourse/pickgit/post/application/search/type/SearchType.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.post.application.search.type; 2 | 3 | import com.woowacourse.pickgit.post.domain.Post; 4 | import java.util.List; 5 | import org.springframework.data.domain.Pageable; 6 | 7 | public interface SearchType { 8 | 9 | boolean isSatisfiedBy(String searchType); 10 | List search(String keywords, Pageable pageable); 11 | } 12 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/java/com/woowacourse/pickgit/post/domain/Posts.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.post.domain; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import javax.persistence.Embeddable; 6 | import javax.persistence.FetchType; 7 | import javax.persistence.OneToMany; 8 | 9 | @Embeddable 10 | public class Posts { 11 | 12 | @OneToMany(mappedBy = "user", fetch = FetchType.LAZY, orphanRemoval = true) 13 | private List posts; 14 | 15 | public Posts() { 16 | this(new ArrayList<>()); 17 | } 18 | 19 | public Posts(List posts) { 20 | this.posts = posts; 21 | } 22 | 23 | public int count() { 24 | return posts.size(); 25 | } 26 | 27 | public List getPosts() { 28 | return posts; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/java/com/woowacourse/pickgit/post/domain/repository/PickGitStorage.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.post.domain.repository; 2 | 3 | import java.io.File; 4 | import java.util.List; 5 | import org.springframework.web.multipart.MultipartFile; 6 | 7 | public interface PickGitStorage { 8 | 9 | List store(List files, String userName); 10 | List storeMultipartFile(List multipartFiles, String userName); 11 | } 12 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/java/com/woowacourse/pickgit/post/domain/util/PlatformRepositoryApiRequester.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.post.domain.util; 2 | 3 | public interface PlatformRepositoryApiRequester { 4 | 5 | String request(String token, String url); 6 | } 7 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/java/com/woowacourse/pickgit/post/domain/util/PlatformRepositoryExtractor.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.post.domain.util; 2 | 3 | import com.woowacourse.pickgit.post.domain.util.dto.RepositoryNameAndUrl; 4 | import java.util.List; 5 | import org.springframework.data.domain.Pageable; 6 | 7 | public interface PlatformRepositoryExtractor { 8 | 9 | List extract(String token, String username, Pageable pageable); 10 | } 11 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/java/com/woowacourse/pickgit/post/domain/util/PlatformRepositorySearchExtractor.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.post.domain.util; 2 | 3 | import com.woowacourse.pickgit.post.domain.util.dto.RepositoryNameAndUrl; 4 | import java.util.List; 5 | import org.springframework.data.domain.Pageable; 6 | 7 | public interface PlatformRepositorySearchExtractor { 8 | 9 | List extract( 10 | String token, 11 | String username, 12 | String keyword, 13 | Pageable pageable 14 | ); 15 | } 16 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/java/com/woowacourse/pickgit/post/domain/util/RestClient.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.post.domain.util; 2 | 3 | import org.springframework.web.client.RestOperations; 4 | 5 | public interface RestClient extends RestOperations { 6 | } 7 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/java/com/woowacourse/pickgit/post/domain/util/dto/RepositoryNameAndUrl.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.post.domain.util.dto; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | 5 | public class RepositoryNameAndUrl { 6 | 7 | private String name; 8 | 9 | @JsonProperty("html_url") 10 | private String url; 11 | 12 | private RepositoryNameAndUrl() { 13 | } 14 | 15 | public RepositoryNameAndUrl(String name, String url) { 16 | this.name = name; 17 | this.url = url; 18 | } 19 | 20 | public String getName() { 21 | return name; 22 | } 23 | 24 | public String getUrl() { 25 | return url; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/java/com/woowacourse/pickgit/post/domain/util/dto/SearchRepositoryNameAndUrl.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.post.domain.util.dto; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | 5 | public class SearchRepositoryNameAndUrl { 6 | 7 | private String name; 8 | 9 | @JsonProperty("html_url") 10 | private String url; 11 | 12 | private SearchRepositoryNameAndUrl() { 13 | } 14 | 15 | public SearchRepositoryNameAndUrl(String name, String url) { 16 | this.name = name; 17 | this.url = url; 18 | } 19 | 20 | public String getName() { 21 | return name; 22 | } 23 | 24 | public String getUrl() { 25 | return url; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/java/com/woowacourse/pickgit/post/infrastructure/dto/RepositoryItemDto.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.post.infrastructure.dto; 2 | 3 | import com.woowacourse.pickgit.post.domain.util.dto.RepositoryNameAndUrl; 4 | import java.util.List; 5 | 6 | public class RepositoryItemDto { 7 | 8 | private List items; 9 | 10 | private RepositoryItemDto() { 11 | } 12 | 13 | public RepositoryItemDto(List items) { 14 | this.items = items; 15 | } 16 | 17 | public List getItems() { 18 | return items; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/java/com/woowacourse/pickgit/post/presentation/dto/request/SearchPostsRequest.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.post.presentation.dto.request; 2 | 3 | public class SearchPostsRequest { 4 | 5 | private String type; 6 | private String keyword; 7 | 8 | private SearchPostsRequest() { 9 | } 10 | 11 | public SearchPostsRequest(String type, String keyword, int page, int limit) { 12 | this.type = type; 13 | this.keyword = keyword; 14 | } 15 | 16 | public String getType() { 17 | return type; 18 | } 19 | 20 | public String getKeyword() { 21 | return keyword; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/java/com/woowacourse/pickgit/post/presentation/dto/response/LikeResponse.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.post.presentation.dto.response; 2 | 3 | import lombok.Builder; 4 | 5 | @Builder 6 | public class LikeResponse { 7 | 8 | private int likesCount; 9 | private Boolean liked; 10 | 11 | private LikeResponse() { 12 | } 13 | 14 | public LikeResponse(int likeCount, boolean liked) { 15 | this.likesCount = likeCount; 16 | this.liked = liked; 17 | } 18 | 19 | public int getLikesCount() { 20 | return likesCount; 21 | } 22 | 23 | public Boolean getLiked() { 24 | return liked; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/java/com/woowacourse/pickgit/post/presentation/dto/response/PostUpdateResponse.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.post.presentation.dto.response; 2 | 3 | import java.util.List; 4 | import lombok.Builder; 5 | 6 | @Builder 7 | public class PostUpdateResponse { 8 | 9 | private List tags; 10 | private String content; 11 | 12 | private PostUpdateResponse() { 13 | } 14 | 15 | public PostUpdateResponse(List tags, String content) { 16 | this.tags = tags; 17 | this.content = content; 18 | } 19 | 20 | public List getTags() { 21 | return tags; 22 | } 23 | 24 | public String getContent() { 25 | return content; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/java/com/woowacourse/pickgit/post/presentation/dto/response/RepositoryResponse.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.post.presentation.dto.response; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import lombok.Builder; 5 | 6 | @Builder 7 | public class RepositoryResponse { 8 | 9 | @JsonProperty("html_url") 10 | private String url; 11 | 12 | private String name; 13 | 14 | private RepositoryResponse() { 15 | } 16 | 17 | public RepositoryResponse(String url, String name) { 18 | this.url = url; 19 | this.name = name; 20 | } 21 | 22 | public String getUrl() { 23 | return url; 24 | } 25 | 26 | public String getName() { 27 | return name; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/java/com/woowacourse/pickgit/post/presentation/postfeed/FeedType.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.post.presentation.postfeed; 2 | 3 | import com.woowacourse.pickgit.post.application.dto.request.HomeFeedRequestDto; 4 | import com.woowacourse.pickgit.post.application.dto.response.PostResponseDto; 5 | import java.util.List; 6 | 7 | public interface FeedType { 8 | 9 | boolean isSatisfiedBy(String type); 10 | 11 | List find(HomeFeedRequestDto homeFeedRequestDto); 12 | } 13 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/java/com/woowacourse/pickgit/tag/application/dto/TagsDto.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.tag.application.dto; 2 | 3 | import java.util.Collections; 4 | import java.util.List; 5 | import java.util.Optional; 6 | 7 | public class TagsDto { 8 | 9 | private List tagNames; 10 | 11 | private TagsDto() { 12 | } 13 | 14 | public TagsDto(List tagNames) { 15 | this.tagNames = tagNames; 16 | } 17 | 18 | public List getTagNames() { 19 | return Optional.ofNullable(tagNames) 20 | .orElseGet(Collections::emptyList); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/java/com/woowacourse/pickgit/tag/domain/PlatformTagExtractor.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.tag.domain; 2 | 3 | import java.util.List; 4 | 5 | public interface PlatformTagExtractor { 6 | 7 | List extractTags(String accessToken, String userName, String repositoryName); 8 | } 9 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/java/com/woowacourse/pickgit/tag/domain/TagRepository.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.tag.domain; 2 | 3 | import java.util.List; 4 | import java.util.Optional; 5 | import org.springframework.data.jpa.repository.JpaRepository; 6 | 7 | public interface TagRepository extends JpaRepository { 8 | 9 | Optional findByName(String name); 10 | 11 | List findTagByNameIn(List tagNames); 12 | } 13 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/java/com/woowacourse/pickgit/tag/infrastructure/PlatformTagApiRequester.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.tag.infrastructure; 2 | 3 | public interface PlatformTagApiRequester { 4 | 5 | String requestTags(String url, String accessToken); 6 | } 7 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/java/com/woowacourse/pickgit/tag/presentation/dto/TagAssembler.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.tag.presentation.dto; 2 | 3 | import com.woowacourse.pickgit.authentication.domain.user.AppUser; 4 | import com.woowacourse.pickgit.tag.application.dto.ExtractionRequestDto; 5 | 6 | public class TagAssembler { 7 | 8 | public static ExtractionRequestDto extractionRequestDto( 9 | AppUser appUser, 10 | String repositoryName 11 | ) { 12 | return ExtractionRequestDto.builder() 13 | .accessToken(appUser.getAccessToken()) 14 | .userName(appUser.getUsername()) 15 | .repositoryName(repositoryName) 16 | .build(); 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/java/com/woowacourse/pickgit/user/application/dto/request/ProfileImageEditRequestDto.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.user.application.dto.request; 2 | 3 | import lombok.Builder; 4 | import lombok.Getter; 5 | 6 | @Builder 7 | @Getter 8 | public class ProfileImageEditRequestDto { 9 | 10 | private byte[] image; 11 | 12 | private ProfileImageEditRequestDto() { 13 | } 14 | 15 | public ProfileImageEditRequestDto(byte[] image) { 16 | this.image = image; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/java/com/woowacourse/pickgit/user/application/dto/response/FollowResponseDto.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.user.application.dto.response; 2 | 3 | import lombok.Builder; 4 | import lombok.Getter; 5 | 6 | @Builder 7 | @Getter 8 | public class FollowResponseDto { 9 | 10 | private int followerCount; 11 | private boolean isFollowing; 12 | 13 | private FollowResponseDto() { 14 | } 15 | 16 | public FollowResponseDto(int followerCount, boolean isFollowing) { 17 | this.followerCount = followerCount; 18 | this.isFollowing = isFollowing; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/java/com/woowacourse/pickgit/user/application/dto/response/ProfileImageEditResponseDto.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.user.application.dto.response; 2 | 3 | import lombok.Builder; 4 | import lombok.Getter; 5 | 6 | @Builder 7 | @Getter 8 | public class ProfileImageEditResponseDto { 9 | 10 | private String imageUrl; 11 | 12 | private ProfileImageEditResponseDto() { 13 | } 14 | 15 | public ProfileImageEditResponseDto(String imageUrl) { 16 | this.imageUrl = imageUrl; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/java/com/woowacourse/pickgit/user/domain/contribution/ContributionCategory.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.user.domain.contribution; 2 | 3 | public enum ContributionCategory { 4 | STAR, COMMIT, PR, ISSUE, REPO 5 | } 6 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/java/com/woowacourse/pickgit/user/domain/contribution/PlatformContributionCalculator.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.user.domain.contribution; 2 | 3 | public interface PlatformContributionCalculator { 4 | 5 | Contribution calculate(String accessToken, String username); 6 | } 7 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/java/com/woowacourse/pickgit/user/domain/follow/PlatformFollowingRequester.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.user.domain.follow; 2 | 3 | public interface PlatformFollowingRequester { 4 | 5 | void follow(String targetName, String accessToken); 6 | void unfollow(String targetName, String accessToken); 7 | } 8 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/java/com/woowacourse/pickgit/user/domain/profile/PickGitProfileStorage.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.user.domain.profile; 2 | 3 | import java.io.File; 4 | import java.util.Optional; 5 | 6 | public interface PickGitProfileStorage { 7 | 8 | Optional store(File file, String userName); 9 | String storeByteFile(byte[] file, String userName); 10 | } 11 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/java/com/woowacourse/pickgit/user/domain/search/CustomUserSearchEngine.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.user.domain.search; 2 | 3 | import com.woowacourse.pickgit.user.domain.User; 4 | import java.util.List; 5 | import org.springframework.data.domain.Pageable; 6 | 7 | public interface CustomUserSearchEngine { 8 | 9 | List searchByUsernameLike(String username, Pageable pageable); 10 | } 11 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/java/com/woowacourse/pickgit/user/domain/search/UserSearchEngine.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.user.domain.search; 2 | 3 | import com.woowacourse.pickgit.user.domain.User; 4 | import org.springframework.data.elasticsearch.repository.ElasticsearchRepository; 5 | 6 | public interface UserSearchEngine extends ElasticsearchRepository, CustomUserSearchEngine { 7 | } 8 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/java/com/woowacourse/pickgit/user/infrastructure/dto/CountDto.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.user.infrastructure.dto; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import lombok.Builder; 5 | 6 | @Builder 7 | public class CountDto { 8 | 9 | @JsonProperty("total_count") 10 | private int count; 11 | 12 | private CountDto() { 13 | } 14 | 15 | public CountDto(int count) { 16 | this.count = count; 17 | } 18 | 19 | public int getCount() { 20 | return count; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/java/com/woowacourse/pickgit/user/infrastructure/dto/ItemDto.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.user.infrastructure.dto; 2 | 3 | import java.util.List; 4 | 5 | public class ItemDto { 6 | 7 | private List items; 8 | 9 | private ItemDto() { 10 | } 11 | 12 | public ItemDto(List items) { 13 | this.items = items; 14 | } 15 | 16 | public List getItems() { 17 | return items; 18 | } 19 | 20 | public int sum() { 21 | return items.stream() 22 | .mapToInt(StarsDto::getStars) 23 | .sum(); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/java/com/woowacourse/pickgit/user/infrastructure/dto/StarsDto.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.user.infrastructure.dto; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import lombok.Builder; 5 | 6 | @Builder 7 | public class StarsDto { 8 | 9 | @JsonProperty("stargazers_count") 10 | private int stars; 11 | 12 | private StarsDto() { 13 | } 14 | 15 | public StarsDto(int stars) { 16 | this.stars = stars; 17 | } 18 | 19 | public int getStars() { 20 | return stars; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/java/com/woowacourse/pickgit/user/presentation/dto/request/ContributionRequestDto.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.user.presentation.dto.request; 2 | 3 | import lombok.Builder; 4 | 5 | @Builder 6 | public class ContributionRequestDto { 7 | 8 | private String accessToken; 9 | private String username; 10 | 11 | private ContributionRequestDto() { 12 | } 13 | 14 | public ContributionRequestDto(String accessToken, String username) { 15 | this.accessToken = accessToken; 16 | this.username = username; 17 | } 18 | 19 | public String getAccessToken() { 20 | return accessToken; 21 | } 22 | 23 | public String getUsername() { 24 | return username; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/java/com/woowacourse/pickgit/user/presentation/dto/request/ProfileDescriptionRequest.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.user.presentation.dto.request; 2 | 3 | import javax.validation.constraints.NotNull; 4 | import javax.validation.constraints.Size; 5 | 6 | public class ProfileDescriptionRequest { 7 | 8 | @NotNull 9 | @Size(max = 160) 10 | private String description; 11 | 12 | private ProfileDescriptionRequest() { 13 | } 14 | 15 | public ProfileDescriptionRequest(String description) { 16 | this.description = description; 17 | } 18 | 19 | public String getDescription() { 20 | return description; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/java/com/woowacourse/pickgit/user/presentation/dto/response/FollowResponse.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.user.presentation.dto.response; 2 | 3 | import lombok.Builder; 4 | import lombok.Getter; 5 | 6 | @Builder 7 | @Getter 8 | public class FollowResponse { 9 | 10 | private int followerCount; 11 | private boolean following; 12 | 13 | private FollowResponse() { 14 | } 15 | 16 | public FollowResponse(int followerCount, boolean isFollowing) { 17 | this.followerCount = followerCount; 18 | this.following = isFollowing; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/java/com/woowacourse/pickgit/user/presentation/dto/response/ProfileDescriptionResponse.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.user.presentation.dto.response; 2 | 3 | import lombok.Builder; 4 | 5 | @Builder 6 | public class ProfileDescriptionResponse { 7 | 8 | private String description; 9 | 10 | private ProfileDescriptionResponse() { 11 | } 12 | 13 | public ProfileDescriptionResponse(String description) { 14 | this.description = description; 15 | } 16 | 17 | public String getDescription() { 18 | return description; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/java/com/woowacourse/pickgit/user/presentation/dto/response/ProfileImageEditResponse.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.user.presentation.dto.response; 2 | 3 | public class ProfileImageEditResponse { 4 | 5 | private String imageUrl; 6 | 7 | private ProfileImageEditResponse() { 8 | } 9 | 10 | public ProfileImageEditResponse(String imageUrl) { 11 | this.imageUrl = imageUrl; 12 | } 13 | 14 | public String getImageUrl() { 15 | return imageUrl; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/resources/access/test-access-logger.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | utf8 6 | 7 | %n###### HTTP Request ###### %n%fullRequest###### HTTP Response ###### %n%fullResponse 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | data: 3 | web: 4 | pageable: 5 | size-parameter: limit 6 | profiles: 7 | include: security 8 | flyway: 9 | enabled: false 10 | server: 11 | compression: 12 | enabled: true 13 | mime-types: application/json 14 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/resources/binary/redis/redis-server-6.2.5-mac-arm64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woowacourse-teams/2021-pick-git/79b2c28bba8b72b30ef50c5aeecb904dcfba0c93/backend/pick-git/src/main/resources/binary/redis/redis-server-6.2.5-mac-arm64 -------------------------------------------------------------------------------- /backend/pick-git/src/main/resources/db/migration/V3__add_user_to_portfolio.sql: -------------------------------------------------------------------------------- 1 | alter table portfolio add column user_id bigint not null; 2 | 3 | alter table portfolio 4 | add constraint fk_portfolio_to_user 5 | foreign key (user_id) 6 | references user (id); 7 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/resources/db/migration/V4__add_created_at_and_updated_at_to_portfolio.sql: -------------------------------------------------------------------------------- 1 | alter table portfolio add column created_at timestamp not null; 2 | alter table portfolio add column updated_at timestamp not null; 3 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/resources/db/migration/V5__add_name_to_portfolio.sql: -------------------------------------------------------------------------------- 1 | alter table portfolio add column name varchar(255) not null; 2 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/resources/db/migration/V6__add_unique_constraint_to_project_tag.sql: -------------------------------------------------------------------------------- 1 | alter table project_tag 2 | add constraint uk_project_tag_tag_project unique (tag_id, project_id); 3 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/resources/db/migration/V7__add_unique_constraint_to_user_name.sql: -------------------------------------------------------------------------------- 1 | alter table user 2 | add constraint uk_user_name unique (name); 3 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/resources/db/migration/V8__update_portfolio_column_type.sql: -------------------------------------------------------------------------------- 1 | alter table comment modify content longtext; 2 | alter table description modify value longtext; 3 | alter table item modify category longtext; 4 | alter table portfolio modify introduction longtext; 5 | alter table post modify content longtext; 6 | alter table project modify content longtext; 7 | -------------------------------------------------------------------------------- /backend/pick-git/src/main/resources/logback-access.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /backend/pick-git/src/test/java/com/woowacourse/pickgit/common/PickgitHeaders.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.common; 2 | 3 | public class PickgitHeaders { 4 | public static String GITHUB_REPO_URL = "githubRepoUrl"; 5 | public static String CONTENT = "content"; 6 | public static String TAGS = "tags"; 7 | public static String IMAGES = "images"; 8 | public static String GITHUB_FOLLOWING = "githubFollowing"; 9 | public static String DESCRIPTION = "description"; 10 | } 11 | -------------------------------------------------------------------------------- /backend/pick-git/src/test/java/com/woowacourse/pickgit/common/mockapi/MockPlatformFollowingRequester.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.common.mockapi; 2 | 3 | import com.woowacourse.pickgit.user.domain.follow.PlatformFollowingRequester; 4 | 5 | public class MockPlatformFollowingRequester implements PlatformFollowingRequester { 6 | private static final String URL_FORMAT = "https://api.github.com/user/following/%s"; 7 | 8 | @Override 9 | public void follow(String targetName, String accessToken) { 10 | } 11 | 12 | @Override 13 | public void unfollow(String targetName, String accessToken) { 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /backend/pick-git/src/test/java/com/woowacourse/pickgit/config/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woowacourse-teams/2021-pick-git/79b2c28bba8b72b30ef50c5aeecb904dcfba0c93/backend/pick-git/src/test/java/com/woowacourse/pickgit/config/.DS_Store -------------------------------------------------------------------------------- /backend/pick-git/src/test/java/com/woowacourse/pickgit/config/SearchEngineCleaner.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.config; 2 | 3 | import com.woowacourse.pickgit.user.domain.search.UserSearchEngine; 4 | import org.springframework.beans.factory.InitializingBean; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.context.annotation.Profile; 7 | import org.springframework.stereotype.Component; 8 | 9 | @Profile("test") 10 | @Component 11 | public class SearchEngineCleaner implements InitializingBean { 12 | 13 | @Autowired 14 | private UserSearchEngine userSearchEngine; 15 | 16 | @Override 17 | public void afterPropertiesSet() { 18 | userSearchEngine.deleteAll(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /backend/pick-git/src/test/java/com/woowacourse/pickgit/config/auth_interceptor_register/scanner/test_classes/inner/ClassFour.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.config.auth_interceptor_register.scanner.test_classes.inner; 2 | 3 | public class ClassFour { 4 | 5 | public void test1() { 6 | 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /backend/pick-git/src/test/java/com/woowacourse/pickgit/config/count_data_source/Count.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.config.count_data_source; 2 | 3 | public class Count { 4 | 5 | private long value; 6 | 7 | public Count(long value) { 8 | this.value = value; 9 | } 10 | 11 | public Count countOne() { 12 | return new Count(++value); 13 | } 14 | 15 | public long getValue() { 16 | return value; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /backend/pick-git/src/test/java/com/woowacourse/pickgit/config/count_data_source/QueryCounter.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.config.count_data_source; 2 | 3 | import lombok.Getter; 4 | 5 | @Getter 6 | public class QueryCounter { 7 | 8 | private Count count; 9 | private boolean countable; 10 | 11 | public QueryCounter() { 12 | this.count = new Count(0L); 13 | countable = false; 14 | } 15 | 16 | public void startCount() { 17 | this.countable = true; 18 | this.count = new Count(0L); 19 | } 20 | 21 | public void countOne() { 22 | count = count.countOne(); 23 | } 24 | 25 | public void endCount() { 26 | this.countable = false; 27 | } 28 | 29 | public boolean isCountable() { 30 | return countable; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /backend/pick-git/src/test/java/com/woowacourse/pickgit/config/db/ReplicationRoutingDataSource.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.pickgit.config.db; 2 | 3 | import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; 4 | 5 | public class ReplicationRoutingDataSource extends AbstractRoutingDataSource { 6 | 7 | private final DataSourceSelector dataSourceSelector; 8 | 9 | public ReplicationRoutingDataSource(DataSourceSelector dataSourceSelector) { 10 | this.dataSourceSelector = dataSourceSelector; 11 | } 12 | 13 | @Override 14 | protected String determineCurrentLookupKey() { 15 | return dataSourceSelector.getSelected(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /backend/pick-git/src/test/resources/testImage1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woowacourse-teams/2021-pick-git/79b2c28bba8b72b30ef50c5aeecb904dcfba0c93/backend/pick-git/src/test/resources/testImage1.png -------------------------------------------------------------------------------- /backend/pick-git/src/test/resources/testImage2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woowacourse-teams/2021-pick-git/79b2c28bba8b72b30ef50c5aeecb904dcfba0c93/backend/pick-git/src/test/resources/testImage2.png -------------------------------------------------------------------------------- /backend/pick-git/src/test/resources/testjar.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woowacourse-teams/2021-pick-git/79b2c28bba8b72b30ef50c5aeecb904dcfba0c93/backend/pick-git/src/test/resources/testjar.jar -------------------------------------------------------------------------------- /frontend/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | dist/ 3 | .vscode/ 4 | yarn-error.log 5 | .env 6 | -------------------------------------------------------------------------------- /frontend/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": true, 3 | "trailingComma": "es5", 4 | "singleQuote": false, 5 | "jsxSingleQuote": false, 6 | "printWidth": 120, 7 | "tabWidth": 2, 8 | "endOfLine": "auto" 9 | } 10 | -------------------------------------------------------------------------------- /frontend/.storybook/main.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | stories: ["../src/**/*.stories.mdx", "../src/**/*.stories.@(js|jsx|ts|tsx)"], 3 | addons: ["@storybook/addon-links", "@storybook/addon-essentials"], 4 | webpackFinal: async (config) => { 5 | config.module.rules.unshift({ 6 | test: /\.svg$/, 7 | use: ["@svgr/webpack", "url-loader"], 8 | }); 9 | return config; 10 | }, 11 | }; 12 | -------------------------------------------------------------------------------- /frontend/.storybook/utils/LoggedInWrapper.tsx: -------------------------------------------------------------------------------- 1 | import { useContext, useEffect } from "react"; 2 | import UserContext, { UserContextProvider } from "../../src/contexts/UserContext"; 3 | 4 | const LoggedInWrapper = ({ children }: { children: React.ReactNode }) => { 5 | const Inner = () => { 6 | const { login } = useContext(UserContext); 7 | 8 | useEffect(() => login("test", "Tanney"), []); 9 | 10 | return <>; 11 | }; 12 | 13 | return ( 14 | 15 | 16 | {children} 17 | 18 | ); 19 | }; 20 | 21 | export default LoggedInWrapper; -------------------------------------------------------------------------------- /frontend/.storybook/utils/components.tsx: -------------------------------------------------------------------------------- 1 | import styled from "styled-components"; 2 | 3 | export const TextEditorWrapper = styled.div` 4 | border-radius: 4px; 5 | padding: 1.1rem 1rem; 6 | background-color: #efefef; 7 | height: 200px; 8 | `; 9 | -------------------------------------------------------------------------------- /frontend/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woowacourse-teams/2021-pick-git/79b2c28bba8b72b30ef50c5aeecb904dcfba0c93/frontend/README.md -------------------------------------------------------------------------------- /frontend/babel.config.ts: -------------------------------------------------------------------------------- 1 | const config = { 2 | presets: ["@babel/preset-env", "@babel/preset-react", "@babel/preset-typescript"], 3 | }; 4 | 5 | export default config; 6 | -------------------------------------------------------------------------------- /frontend/jest.config.ts: -------------------------------------------------------------------------------- 1 | import type { Config } from "@jest/types"; 2 | 3 | const config: Config.InitialOptions = { 4 | verbose: true, 5 | roots: ["/src"], 6 | testMatch: ["**/?(*.)+(test).+(ts|tsx)"], 7 | transform: { 8 | "^.+\\.(ts|tsx)$": "ts-jest", 9 | }, 10 | setupFilesAfterEnv: ["/jestSetup.ts"], 11 | testEnvironment: "jsdom", 12 | }; 13 | 14 | export default config; 15 | -------------------------------------------------------------------------------- /frontend/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woowacourse-teams/2021-pick-git/79b2c28bba8b72b30ef50c5aeecb904dcfba0c93/frontend/public/favicon.ico -------------------------------------------------------------------------------- /frontend/src/@types/assets.d.ts: -------------------------------------------------------------------------------- 1 | declare module "*.svg"; 2 | declare module "*.png"; 3 | declare module "*.jpg"; 4 | declare module "*.tiff"; 5 | declare module "*.jpeg"; 6 | declare module "*.ttf" { 7 | const content: any; 8 | export default content; 9 | } 10 | -------------------------------------------------------------------------------- /frontend/src/@types/external.d.ts: -------------------------------------------------------------------------------- 1 | declare module "https://developers.kakao.com/sdk/js/kakao.min.js"; 2 | 3 | declare namespace Kakao { 4 | function init(apiKey: string): void; 5 | const Link: Object & { 6 | sendDefault(templateObj: Object): void; 7 | }; 8 | } 9 | -------------------------------------------------------------------------------- /frontend/src/@types/react-app-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | /// 4 | 5 | import "styled-components"; 6 | import { theme } from "../App.style"; 7 | import { CSSProp } from "styled-components"; 8 | 9 | type StyledTheme = typeof theme; 10 | 11 | declare module "styled-components" { 12 | export interface DefaultTheme extends StyledTheme {} 13 | } 14 | 15 | declare interface ObjectConstructor { 16 | keys(obj: T): Array; 17 | } 18 | 19 | -------------------------------------------------------------------------------- /frontend/src/assets/font/NanumBarunGothic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woowacourse-teams/2021-pick-git/79b2c28bba8b72b30ef50c5aeecb904dcfba0c93/frontend/src/assets/font/NanumBarunGothic.ttf -------------------------------------------------------------------------------- /frontend/src/assets/font/NanumBarunGothicBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woowacourse-teams/2021-pick-git/79b2c28bba8b72b30ef50c5aeecb904dcfba0c93/frontend/src/assets/font/NanumBarunGothicBold.ttf -------------------------------------------------------------------------------- /frontend/src/assets/font/NanumGothic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woowacourse-teams/2021-pick-git/79b2c28bba8b72b30ef50c5aeecb904dcfba0c93/frontend/src/assets/font/NanumGothic.ttf -------------------------------------------------------------------------------- /frontend/src/assets/font/NanumGothicBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woowacourse-teams/2021-pick-git/79b2c28bba8b72b30ef50c5aeecb904dcfba0c93/frontend/src/assets/font/NanumGothicBold.ttf -------------------------------------------------------------------------------- /frontend/src/assets/font/NotoSansKR-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woowacourse-teams/2021-pick-git/79b2c28bba8b72b30ef50c5aeecb904dcfba0c93/frontend/src/assets/font/NotoSansKR-Regular.ttf -------------------------------------------------------------------------------- /frontend/src/assets/icons/add-box.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /frontend/src/assets/icons/add-circle-large.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /frontend/src/assets/icons/add-circle.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /frontend/src/assets/icons/arrow-down.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /frontend/src/assets/icons/arrow-right.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /frontend/src/assets/icons/blog.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /frontend/src/assets/icons/book.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /frontend/src/assets/icons/briefcase-solid.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/src/assets/icons/camera.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/src/assets/icons/cancel-no-circle.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /frontend/src/assets/icons/clock.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /frontend/src/assets/icons/company-line.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /frontend/src/assets/icons/copy.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /frontend/src/assets/icons/delete-circle.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /frontend/src/assets/icons/delete.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /frontend/src/assets/icons/edit.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /frontend/src/assets/icons/email.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /frontend/src/assets/icons/github-large.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /frontend/src/assets/icons/github-line.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /frontend/src/assets/icons/github-repository.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /frontend/src/assets/icons/go-back.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /frontend/src/assets/icons/go-down.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /frontend/src/assets/icons/go-forward.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /frontend/src/assets/icons/heart-line.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /frontend/src/assets/icons/heart.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /frontend/src/assets/icons/home.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /frontend/src/assets/icons/issue.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /frontend/src/assets/icons/link.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /frontend/src/assets/icons/location-line.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /frontend/src/assets/icons/login.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /frontend/src/assets/icons/map-marker-alt-solid.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/src/assets/icons/person.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /frontend/src/assets/icons/phone.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /frontend/src/assets/icons/post-heart-line.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /frontend/src/assets/icons/post-heart.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /frontend/src/assets/icons/search.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /frontend/src/assets/icons/send.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /frontend/src/assets/icons/star.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /frontend/src/assets/icons/vertical-dots.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /frontend/src/assets/images/app-background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woowacourse-teams/2021-pick-git/79b2c28bba8b72b30ef50c5aeecb904dcfba0c93/frontend/src/assets/images/app-background.png -------------------------------------------------------------------------------- /frontend/src/assets/images/cannot-find.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woowacourse-teams/2021-pick-git/79b2c28bba8b72b30ef50c5aeecb904dcfba0c93/frontend/src/assets/images/cannot-find.png -------------------------------------------------------------------------------- /frontend/src/assets/images/comment-not-found.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woowacourse-teams/2021-pick-git/79b2c28bba8b72b30ef50c5aeecb904dcfba0c93/frontend/src/assets/images/comment-not-found.jpg -------------------------------------------------------------------------------- /frontend/src/assets/images/default-image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woowacourse-teams/2021-pick-git/79b2c28bba8b72b30ef50c5aeecb904dcfba0c93/frontend/src/assets/images/default-image.png -------------------------------------------------------------------------------- /frontend/src/assets/images/default-profile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woowacourse-teams/2021-pick-git/79b2c28bba8b72b30ef50c5aeecb904dcfba0c93/frontend/src/assets/images/default-profile.png -------------------------------------------------------------------------------- /frontend/src/assets/images/empty-post-image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woowacourse-teams/2021-pick-git/79b2c28bba8b72b30ef50c5aeecb904dcfba0c93/frontend/src/assets/images/empty-post-image.png -------------------------------------------------------------------------------- /frontend/src/assets/images/post-not-found.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woowacourse-teams/2021-pick-git/79b2c28bba8b72b30ef50c5aeecb904dcfba0c93/frontend/src/assets/images/post-not-found.png -------------------------------------------------------------------------------- /frontend/src/assets/images/user-not-found.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woowacourse-teams/2021-pick-git/79b2c28bba8b72b30ef50c5aeecb904dcfba0c93/frontend/src/assets/images/user-not-found.png -------------------------------------------------------------------------------- /frontend/src/components/@layout/AlertPortal/AlertPortal.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Story } from "@storybook/react"; 2 | import { Alert, Props } from "./AlertPortal"; 3 | 4 | export default { 5 | title: "Components/Layout/AlertPortal", 6 | component: Alert, 7 | }; 8 | 9 | const Template: Story = (args) => ; 10 | 11 | export const Default = Template.bind({}); 12 | Default.args = { 13 | heading: "제목", 14 | onOkay: () => {}, 15 | }; 16 | -------------------------------------------------------------------------------- /frontend/src/components/@layout/BottomSliderPortal/BottomSliderPortal.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Story } from "@storybook/react"; 2 | import BottomSliderPortal, { Props } from "./BottomSliderPortal"; 3 | 4 | export default { 5 | title: "Components/BottomSliderPortal", 6 | component: BottomSliderPortal, 7 | }; 8 | 9 | const Template: Story = (args) => ; 10 | 11 | export const Default = Template.bind({}); 12 | Default.args = {}; 13 | -------------------------------------------------------------------------------- /frontend/src/components/@layout/ChoiceModalPortal/ChoiceModalPortal.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Story } from "@storybook/react"; 2 | import { ChoiceModal, Props } from "./ChoiceModalPortal"; 3 | 4 | export default { 5 | title: "Components/Layout/Confirm", 6 | component: ChoiceModal, 7 | }; 8 | 9 | const Template: Story = (args) => ; 10 | 11 | export const Default = Template.bind({}); 12 | Default.args = { 13 | heading: "제목", 14 | onClose: () => {}, 15 | }; 16 | -------------------------------------------------------------------------------- /frontend/src/components/@layout/ConfirmPortal/ConfirmPortal.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Story } from "@storybook/react"; 2 | import { Confirm, Props } from "./ConfirmPortal"; 3 | 4 | export default { 5 | title: "Components/Layout/Confirm", 6 | component: Confirm, 7 | }; 8 | 9 | const Template: Story = (args) => ; 10 | 11 | export const Default = Template.bind({}); 12 | Default.args = { 13 | heading: "제목", 14 | onConfirm: () => {}, 15 | }; 16 | -------------------------------------------------------------------------------- /frontend/src/components/@layout/ModalPortal/ModalPortal.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Story } from "@storybook/react"; 2 | 3 | import { Modal, Props } from "./ModalPortal"; 4 | 5 | export default { 6 | title: "Components/Layout/Modal", 7 | component: Modal, 8 | }; 9 | 10 | const Template: Story = (args) => 모달입니다; 11 | 12 | export const Default = Template.bind({}); 13 | Default.args = { 14 | onClose: () => {}, 15 | isCloseButtonShown: true, 16 | }; 17 | -------------------------------------------------------------------------------- /frontend/src/components/@layout/NavigationHeader/NavigationHeader.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Story } from "@storybook/react"; 2 | import LoggedInWrapper from "../../../../.storybook/utils/LoggedInWrapper"; 3 | 4 | import NavigationHeader from "./NavigationHeader"; 5 | 6 | export default { 7 | title: "Components/Layout/NavigationHeader", 8 | component: NavigationHeader, 9 | }; 10 | 11 | const DefaultTemplate: Story = (args) => ; 12 | const LoggedInTemplate: Story = (args) => ( 13 | 14 | 15 | 16 | ); 17 | 18 | export const Default = DefaultTemplate.bind({}); 19 | Default.args = {}; 20 | 21 | export const LoggedIn = LoggedInTemplate.bind({}); 22 | LoggedIn.args = {}; 23 | -------------------------------------------------------------------------------- /frontend/src/components/@layout/PageLoading/PageLoading.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Story } from "@storybook/react"; 2 | 3 | import PageLoading, { Props } from "./PageLoading"; 4 | 5 | export default { 6 | title: "Components/Shared/PageLoading", 7 | component: PageLoading, 8 | }; 9 | 10 | const Template: Story = (args) => 깃들다; 11 | 12 | export const Default = Template.bind({}); 13 | Default.args = {}; 14 | -------------------------------------------------------------------------------- /frontend/src/components/@layout/PageLoading/PageLoading.style.ts: -------------------------------------------------------------------------------- 1 | import styled from "styled-components"; 2 | 3 | export const Container = styled.span` 4 | display: flex; 5 | width: 100%; 6 | height: 100%; 7 | justify-content: center; 8 | align-items: center; 9 | `; 10 | -------------------------------------------------------------------------------- /frontend/src/components/@layout/PageLoading/PageLoading.tsx: -------------------------------------------------------------------------------- 1 | import Loader, { Props as LoaderProps } from "../../@shared/Loader/Loader"; 2 | import { Container } from "./PageLoading.style"; 3 | 4 | export interface Props extends React.HTMLAttributes { 5 | LoaderSize?: string; 6 | LoaderKind?: LoaderProps["kind"]; 7 | } 8 | 9 | const PageLoading = ({ LoaderSize = "2rem", LoaderKind = "spinner", ...props }: Props) => { 10 | return ( 11 | 12 | 13 | 14 | ); 15 | }; 16 | 17 | export default PageLoading; 18 | -------------------------------------------------------------------------------- /frontend/src/components/@layout/PageLoadingWithCover/PageLoadingWithCover.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Story } from "@storybook/react"; 2 | 3 | import PageLoadingWithCover, { Props } from "./PageLoadingWithCover"; 4 | 5 | export default { 6 | title: "Components/Layout/PageLoadingWithCover", 7 | component: PageLoadingWithCover, 8 | }; 9 | 10 | const Template: Story = (args) => ( 11 |
12 | ; 13 |
14 | ); 15 | 16 | export const Default = Template.bind({}); 17 | Default.args = { 18 | description: "로딩중", 19 | }; 20 | -------------------------------------------------------------------------------- /frontend/src/components/@layout/PageLoadingWithCover/PageLoadingWithCover.style.ts: -------------------------------------------------------------------------------- 1 | import styled from "styled-components"; 2 | 3 | export const Container = styled.div` 4 | display: flex; 5 | flex-direction: column; 6 | justify-content: center; 7 | align-items: center; 8 | 9 | position: absolute; 10 | top: 0; 11 | right: 0; 12 | bottom: 0; 13 | left: 0; 14 | 15 | ${({ theme }) => ` 16 | background-color: ${theme.color.white}; 17 | color: ${theme.color.tertiaryColor}; 18 | `} 19 | font-size: 2rem; 20 | font-family: "jua", "Noto Sans KR", sans-serif; 21 | opacity: 0.8; 22 | `; 23 | -------------------------------------------------------------------------------- /frontend/src/components/@layout/PageLoadingWithCover/PageLoadingWithCover.tsx: -------------------------------------------------------------------------------- 1 | import { useContext } from "react"; 2 | import { ThemeContext } from "styled-components"; 3 | import Loader from "../../@shared/Loader/Loader"; 4 | import { Container } from "./PageLoadingWithCover.style"; 5 | 6 | export interface Props { 7 | description: string; 8 | } 9 | 10 | const PageLoadingWithCover = ({ description }: Props) => { 11 | const theme = useContext(ThemeContext); 12 | 13 | return ( 14 | 15 | {description} 16 | 17 | 18 | ); 19 | }; 20 | 21 | export default PageLoadingWithCover; 22 | -------------------------------------------------------------------------------- /frontend/src/components/@layout/PageLoadingWithLogo/PageLoadingWithLogo.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Story } from "@storybook/react"; 2 | 3 | import PageLoadingWithLogo from "./PageLoadingWithLogo"; 4 | 5 | export default { 6 | title: "Components/Layout/PageLoadingWithLogo", 7 | component: PageLoadingWithLogo, 8 | }; 9 | 10 | const Template: Story = () => ; 11 | 12 | export const Default = Template.bind({}); 13 | -------------------------------------------------------------------------------- /frontend/src/components/@layout/PageLoadingWithLogo/PageLoadingWithLogo.style.ts: -------------------------------------------------------------------------------- 1 | import styled from "styled-components"; 2 | import { breathingAnimation } from "../../@styled/keyframes"; 3 | 4 | export const Container = styled.div` 5 | position: fixed; 6 | top: 0; 7 | left: 0; 8 | width: 100%; 9 | height: 100%; 10 | display: flex; 11 | justify-content: center; 12 | align-items: center; 13 | `; 14 | 15 | export const LogoIconWrapper = styled.div` 16 | position: relative; 17 | bottom: 0.4rem; 18 | margin-right: 0.5rem; 19 | animation: ${breathingAnimation} 2s linear infinite; 20 | `; 21 | -------------------------------------------------------------------------------- /frontend/src/components/@layout/PageLoadingWithLogo/PageLoadingWithLogo.tsx: -------------------------------------------------------------------------------- 1 | import { LogoLargeIcon as LogoIcon } from "../../../assets/icons"; 2 | import { Container, LogoIconWrapper } from "./PageLoadingWithLogo.style"; 3 | 4 | const PageLoadingWithLogo = () => { 5 | return ( 6 | 7 | 8 | 9 | 10 | 11 | ); 12 | }; 13 | 14 | export default PageLoadingWithLogo; 15 | -------------------------------------------------------------------------------- /frontend/src/components/@layout/PortfolioHeader/PortfolioHeader.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Story } from "@storybook/react"; 2 | 3 | import PortfolioHeader, { Props } from "./PortfolioHeader"; 4 | 5 | export default { 6 | title: "Components/Shared/PortfolioHeader", 7 | component: PortfolioHeader, 8 | }; 9 | 10 | const Template: Story = (args) => 깃들다; 11 | 12 | export const Default = Template.bind({}); 13 | Default.args = {}; 14 | -------------------------------------------------------------------------------- /frontend/src/components/@layout/ScrollActiveHeader/ScrollActiveHeader.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Story } from "@storybook/react"; 2 | 3 | import ScrollActiveHeader, { Props } from "./ScrollActiveHeader"; 4 | 5 | export default { 6 | title: "Components/Shared/ScrollActiveHeader", 7 | component: ScrollActiveHeader, 8 | }; 9 | 10 | const Template: Story = (args) => 깃들다; 11 | 12 | export const Default = Template.bind({}); 13 | Default.args = {}; 14 | -------------------------------------------------------------------------------- /frontend/src/components/@layout/ScrollActiveHeader/ScrollActiveHeader.style.ts: -------------------------------------------------------------------------------- 1 | import styled from "styled-components"; 2 | import { Z_INDEX } from "../../../constants/layout"; 3 | 4 | export const Container = styled.div<{ isHeaderShown: boolean }>` 5 | position: fixed; 6 | top: 0; 7 | width: 100%; 8 | z-index: ${Z_INDEX.LOW}; 9 | transition: transform 0.5s; 10 | 11 | transform: ${({ isHeaderShown }) => (isHeaderShown ? "translateY(0)" : "translateY(-100%)")}; 12 | `; 13 | -------------------------------------------------------------------------------- /frontend/src/components/@layout/SearchHeader/SearchHeader.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Story } from "@storybook/react"; 2 | 3 | import SearchHeader from "./SearchHeader"; 4 | 5 | export default { 6 | title: "Components/Layout/SearchHeader", 7 | component: SearchHeader, 8 | }; 9 | 10 | const Template: Story = (args) => ; 11 | 12 | export const Default = Template.bind({}); 13 | Default.args = {}; 14 | -------------------------------------------------------------------------------- /frontend/src/components/@layout/Snackbar/Snackbar.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Story, Meta } from "@storybook/react"; 2 | 3 | import SnackBar, { Props } from "./Snackbar"; 4 | 5 | export default { 6 | title: "Components/Shared/SnackBar", 7 | component: SnackBar, 8 | } as Meta; 9 | 10 | const Template: Story = (args) => ; 11 | 12 | export const Order1 = Template.bind({}); 13 | export const Order2 = Template.bind({}); 14 | 15 | Order1.args = { 16 | children: "스낵바입니다.", 17 | order: 1, 18 | }; 19 | 20 | Order2.args = { 21 | children: "스낵바입니다.", 22 | order: 2, 23 | }; 24 | -------------------------------------------------------------------------------- /frontend/src/components/@layout/Snackbar/Snackbar.tsx: -------------------------------------------------------------------------------- 1 | import { Container } from "./Snackbar.style"; 2 | import { SNACKBAR_GAP_REM, SNACKBAR_HEIGHT_REM, SnackBarOrder, SNACKBAR_DURATION } from "../../../constants/snackbar"; 3 | 4 | const getSnackBarBottom = (order: SnackBarOrder) => 5 | `${order * (SNACKBAR_HEIGHT_REM + SNACKBAR_GAP_REM) - SNACKBAR_HEIGHT_REM}rem`; 6 | 7 | export interface Props { 8 | children: string; 9 | order: SnackBarOrder; 10 | } 11 | 12 | const SnackBar = ({ children, order }: Props) => { 13 | return ( 14 | 19 | {children} 20 | 21 | ); 22 | }; 23 | 24 | export default SnackBar; 25 | -------------------------------------------------------------------------------- /frontend/src/components/@layout/StepHeader/StepHeader.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Story } from "@storybook/react"; 2 | 3 | import StepHeader, { Props } from "./StepHeader"; 4 | 5 | export default { 6 | title: "Components/Layout/StepHeader", 7 | component: StepHeader, 8 | }; 9 | 10 | const Template: Story = (args) => Git 리포지터리; 11 | 12 | export const Default = Template.bind({}); 13 | Default.args = {}; 14 | -------------------------------------------------------------------------------- /frontend/src/components/@shared/Avatar/Avatar.tsx: -------------------------------------------------------------------------------- 1 | import { CSSProp } from "styled-components"; 2 | import { CircleImage, Container, Name } from "./Avatar.style"; 3 | 4 | export interface Props { 5 | diameter: string; 6 | fontSize?: string; 7 | imageUrl?: string; 8 | name?: string; 9 | cssProp?: CSSProp; 10 | } 11 | 12 | const Avatar = ({ diameter, fontSize, imageUrl, name, cssProp }: Props) => ( 13 | 14 | 15 | {name && {name}} 16 | 17 | ); 18 | 19 | export default Avatar; 20 | -------------------------------------------------------------------------------- /frontend/src/components/@shared/ButtonDrawer/ButtonDrawer.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Story } from "@storybook/react"; 2 | 3 | import { TrashIcon, EditIcon } from "../../../assets/icons"; 4 | import ButtonDrawer, { Props } from "./ButtonDrawer"; 5 | 6 | export default { 7 | title: "Components/Shared/ButtonDrawer", 8 | component: ButtonDrawer, 9 | }; 10 | 11 | const Template: Story = (args) => ( 12 |
13 | 14 |
15 | ); 16 | 17 | export const Default = Template.bind({}); 18 | Default.args = { 19 | circleButtons: [ 20 | { icon: "EditIcon", onClick: () => alert("click!") }, 21 | { icon: "TrashIcon", onClick: () => alert("click!") }, 22 | ], 23 | }; 24 | -------------------------------------------------------------------------------- /frontend/src/components/@shared/Chip/Chip.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Story } from "@storybook/react"; 2 | 3 | import Chip, { Props } from "./Chip"; 4 | 5 | export default { 6 | title: "Components/Shared/Chip", 7 | component: Chip, 8 | }; 9 | 10 | const Template: Story = (args) => 깃들다; 11 | 12 | export const Default = Template.bind({}); 13 | Default.args = {}; 14 | 15 | export const Deletable = Template.bind({}); 16 | 17 | Deletable.args = { 18 | onDelete: () => alert("삭제됨!"), 19 | }; 20 | -------------------------------------------------------------------------------- /frontend/src/components/@shared/CircleIcon/CircleIcon.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Story } from "@storybook/react"; 2 | 3 | import CircleIcon, { Props } from "./CircleIcon"; 4 | 5 | export default { 6 | title: "Components/Shared/CircleIcon", 7 | component: CircleIcon, 8 | }; 9 | 10 | const Template: Story = (args) => Git; 11 | 12 | export const Default = Template.bind({}); 13 | Default.args = { 14 | diameter: "3rem", 15 | name: "깃들다 들다", 16 | }; 17 | -------------------------------------------------------------------------------- /frontend/src/components/@shared/CircleIcon/CircleIcon.tsx: -------------------------------------------------------------------------------- 1 | import { CircleBackground, Container, Name } from "./CircleIcon.style"; 2 | 3 | export interface Props extends React.HTMLAttributes { 4 | diameter: string; 5 | fontSize?: string; 6 | backgroundColor?: string; 7 | name?: string; 8 | } 9 | 10 | const CircleIcon = ({ diameter, fontSize, backgroundColor, name, children }: Props) => ( 11 | 12 | 13 | {children} 14 | 15 | {name && {name}} 16 | 17 | ); 18 | 19 | export default CircleIcon; 20 | -------------------------------------------------------------------------------- /frontend/src/components/@shared/Comment/Comment.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Story } from "@storybook/react"; 2 | 3 | import Comment, { Props } from "./Comment"; 4 | 5 | export default { 6 | title: "Components/Shared/Comment", 7 | component: Comment, 8 | }; 9 | 10 | const Template: Story = (args) => ; 11 | 12 | export const Default = Template.bind({}); 13 | Default.args = { 14 | authorName: "Tanney", 15 | content: "개발 너무 재미있어 미치겠어", 16 | }; 17 | -------------------------------------------------------------------------------- /frontend/src/components/@shared/Comment/Comment.tsx: -------------------------------------------------------------------------------- 1 | import { Container, AuthorName, Content } from "./Comment.style"; 2 | 3 | export interface Props { 4 | authorName: string; 5 | content: React.ReactNode; 6 | link?: string; 7 | } 8 | 9 | const Comment = ({ authorName, link, content }: Props) => { 10 | return ( 11 | 12 | {authorName} 13 | {content} 14 | 15 | ); 16 | }; 17 | 18 | export default Comment; 19 | -------------------------------------------------------------------------------- /frontend/src/components/@shared/ContributionGraph/ContributionGraph.style.ts: -------------------------------------------------------------------------------- 1 | import styled from "styled-components"; 2 | 3 | export const Container = styled.div<{ columnCount: number; rowCount: number }>` 4 | display: grid; 5 | width: 100%; 6 | height: 100%; 7 | ${({ columnCount, rowCount }) => ` 8 | grid-template-columns: repeat(${columnCount}, 1fr); 9 | grid-template-rows: repeat(${rowCount}, 1fr); 10 | `}; 11 | grid-column-gap: 0.1875rem; 12 | grid-row-gap: 0.1875rem; 13 | grid-auto-flow: column; 14 | `; 15 | 16 | export const ContributionItem = styled.div<{ backgroundColor: string }>` 17 | background-color: ${({ backgroundColor }) => backgroundColor}; 18 | border-radius: 3px; 19 | `; 20 | -------------------------------------------------------------------------------- /frontend/src/components/@shared/CountIndicator/CountIndicator.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Story } from "@storybook/react"; 2 | 3 | import CountIndicator, { Props } from "./CountIndicator"; 4 | 5 | export default { 6 | title: "Components/Shared/CountIndicator", 7 | component: CountIndicator, 8 | }; 9 | 10 | const Template: Story = (args) => ; 11 | 12 | export const Default = Template.bind({}); 13 | 14 | Default.args = { 15 | name: "게시물", 16 | count: 102, 17 | }; 18 | -------------------------------------------------------------------------------- /frontend/src/components/@shared/CountIndicator/CountIndicator.style.ts: -------------------------------------------------------------------------------- 1 | import styled, { css } from "styled-components"; 2 | 3 | export const Container = styled.div` 4 | display: inline-flex; 5 | flex-direction: column; 6 | justify-content: space-between; 7 | align-items: center; 8 | 9 | width: fit-content; 10 | height: 2.6875rem; 11 | line-height: 0.9; 12 | `; 13 | 14 | export const Count = styled.div( 15 | ({ theme }) => css` 16 | font-size: 1rem; 17 | font-weight: bold; 18 | color: ${theme.color.textColor}; 19 | ` 20 | ); 21 | 22 | export const Name = styled.div( 23 | ({ theme }) => css` 24 | font-size: 0.875rem; 25 | color: ${theme.color.textColor}; 26 | ` 27 | ); 28 | -------------------------------------------------------------------------------- /frontend/src/components/@shared/CountIndicator/CountIndicator.tsx: -------------------------------------------------------------------------------- 1 | import { Container, Count, Name } from "./CountIndicator.style"; 2 | 3 | export interface Props { 4 | name: string; 5 | count: number; 6 | } 7 | 8 | const CountIndicator = ({ name, count }: Props) => ( 9 | 10 | {count} 11 | {name} 12 | 13 | ); 14 | 15 | export default CountIndicator; 16 | -------------------------------------------------------------------------------- /frontend/src/components/@shared/DateInput/DateInput.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Story } from "@storybook/react"; 2 | 3 | import DateInput, { Props } from "./DateInput"; 4 | 5 | export default { 6 | title: "Components/Shared/DateInput", 7 | component: DateInput, 8 | }; 9 | 10 | const Template: Story = (args) => 깃들다; 11 | 12 | export const Default = Template.bind({}); 13 | Default.args = {}; 14 | -------------------------------------------------------------------------------- /frontend/src/components/@shared/DateInput/DateInput.style.ts: -------------------------------------------------------------------------------- 1 | import styled, { css, CSSProp } from "styled-components"; 2 | 3 | export const Input = styled.input<{ cssProp?: CSSProp }>( 4 | ({ cssProp }) => css` 5 | ${cssProp} 6 | border: none; 7 | ` 8 | ); 9 | -------------------------------------------------------------------------------- /frontend/src/components/@shared/DateInput/DateInput.tsx: -------------------------------------------------------------------------------- 1 | import { CSSProp } from "styled-components"; 2 | import { Input } from "./DateInput.style"; 3 | 4 | export interface Props extends React.HTMLAttributes { 5 | value: string; 6 | cssProp?: CSSProp; 7 | disabled?: boolean; 8 | onChange: React.ChangeEventHandler; 9 | } 10 | 11 | const DateInput = ({ cssProp, value, disabled, onChange, ...props }: Props) => { 12 | return ; 13 | }; 14 | 15 | export default DateInput; 16 | -------------------------------------------------------------------------------- /frontend/src/components/@shared/DotPaginator/DotPaginator.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Story } from "@storybook/react"; 2 | 3 | import DotPaginator, { Props } from "./DotPaginator"; 4 | 5 | export default { 6 | title: "Components/Shared/DotPaginator", 7 | component: DotPaginator, 8 | }; 9 | 10 | const Template: Story = (args) => 깃들다; 11 | 12 | export const Default = Template.bind({}); 13 | 14 | Default.args = {}; 15 | -------------------------------------------------------------------------------- /frontend/src/components/@shared/DotPaginator/DotPaginator.tsx: -------------------------------------------------------------------------------- 1 | import { useState } from "react"; 2 | import { Container, DotPagination } from "./DotPaginator.style"; 3 | 4 | export interface Props { 5 | activePageIndex: number; 6 | paginationCount: number; 7 | onPaginate: (index: number) => void; 8 | } 9 | 10 | const DotPaginator = ({ activePageIndex, paginationCount, onPaginate }: Props) => { 11 | const paginationItems = [...Array(paginationCount)].map((_, index) => ( 12 | onPaginate(index)} /> 13 | )); 14 | 15 | return {paginationItems}; 16 | }; 17 | 18 | export default DotPaginator; 19 | -------------------------------------------------------------------------------- /frontend/src/components/@shared/DropDown/DropDown.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Story } from "@storybook/react"; 2 | 3 | import DropDown, { Props } from "./DropDown"; 4 | 5 | export default { 6 | title: "Components/Shared/DropDown", 7 | component: DropDown, 8 | }; 9 | 10 | const Template: Story = (args) => 깃들다; 11 | 12 | export const Default = Template.bind({}); 13 | Default.args = {}; 14 | -------------------------------------------------------------------------------- /frontend/src/components/@shared/Fab/Fab.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Story } from "@storybook/react"; 2 | 3 | import Fab, { Props } from "./Fab"; 4 | 5 | export default { 6 | title: "Components/Shared/Fab", 7 | component: Fab, 8 | }; 9 | 10 | const Template: Story = (args) => 깃들다; 11 | 12 | export const Default = Template.bind({}); 13 | Default.args = {}; 14 | -------------------------------------------------------------------------------- /frontend/src/components/@shared/ImageUploader/ImageUploader.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Story } from "@storybook/react"; 2 | 3 | import ImageUploader, { Props } from "./ImageUploader"; 4 | 5 | export default { 6 | title: "Components/Shared/ImageUploader", 7 | component: ImageUploader, 8 | }; 9 | 10 | const Template: Story = (args) => ; 11 | 12 | export const Default = Template.bind({}); 13 | Default.args = { 14 | onFileListSave: (fileList: FileList) => { 15 | const message = Array.from(fileList) 16 | .map((file) => file.name) 17 | .join(",") 18 | .concat(" 이미지를 저장했습니다."); 19 | alert(message); 20 | }, 21 | }; 22 | -------------------------------------------------------------------------------- /frontend/src/components/@shared/ImageUploader/ImageUploader.style.ts: -------------------------------------------------------------------------------- 1 | import styled, { css, CSSProp } from "styled-components"; 2 | 3 | export const Container = styled.div<{ cssProp?: CSSProp }>( 4 | ({ cssProp }) => css` 5 | display: flex; 6 | justify-content: center; 7 | width: 100%; 8 | ${cssProp} 9 | ` 10 | ); 11 | 12 | export const Image = styled.img` 13 | width: 100%; 14 | transition: opacity 0.5s, box-shadow 0.5s; 15 | cursor: pointer; 16 | object-fit: cover; 17 | 18 | :hover { 19 | opacity: 0.85; 20 | box-shadow: 1px 2px 6px rgba(0, 0, 0, 0.2); 21 | } 22 | `; 23 | -------------------------------------------------------------------------------- /frontend/src/components/@shared/InfiniteScrollContainer/InfiniteScrollContainer.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Story } from "@storybook/react"; 2 | 3 | import InfiniteScrollContainer, { Props } from "./InfiniteScrollContainer"; 4 | 5 | export default { 6 | title: "Components/Shared/InfiniteScrollContainer", 7 | component: InfiniteScrollContainer, 8 | }; 9 | 10 | const Template: Story = (args) => 깃들다; 11 | 12 | export const Default = Template.bind({}); 13 | Default.args = { 14 | onIntersect: () => { 15 | alert("교차됨!"); 16 | }, 17 | }; 18 | -------------------------------------------------------------------------------- /frontend/src/components/@shared/InfiniteScrollContainer/InfiniteScrollContainer.style.ts: -------------------------------------------------------------------------------- 1 | import styled from "styled-components"; 2 | 3 | export const Container = styled.div` 4 | width: 100%; 5 | `; 6 | 7 | export const ContentWrapper = styled.div``; 8 | 9 | export const LoaderWrapper = styled.div` 10 | display: flex; 11 | justify-content: center; 12 | padding: 2rem 0; 13 | `; 14 | -------------------------------------------------------------------------------- /frontend/src/components/@shared/Input/Input.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Story } from "@storybook/react"; 2 | import { SearchIcon } from "../../../assets/icons"; 3 | 4 | import Input, { Props } from "./Input"; 5 | 6 | export default { 7 | title: "Components/Shared/Input", 8 | component: Input, 9 | }; 10 | 11 | const Template: Story = (args) => ; 12 | 13 | export const Default = Template.bind({}); 14 | Default.args = {}; 15 | 16 | export const InputWithIcon = Template.bind({}); 17 | InputWithIcon.args = { 18 | icon: , 19 | }; 20 | -------------------------------------------------------------------------------- /frontend/src/components/@shared/Loader/Loader.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Story } from "@storybook/react"; 2 | 3 | import Loader, { Props } from "./Loader"; 4 | 5 | export default { 6 | title: "Components/Shared/Loader", 7 | component: Loader, 8 | }; 9 | 10 | const Template: Story = (args) => 깃들다; 11 | 12 | export const Dots = Template.bind({}); 13 | 14 | export const Spinner = Template.bind({}); 15 | 16 | Dots.args = { 17 | kind: "dots", 18 | size: "1rem", 19 | }; 20 | 21 | Spinner.args = { 22 | kind: "spinner", 23 | size: "1.6rem", 24 | }; 25 | -------------------------------------------------------------------------------- /frontend/src/components/@shared/NotFound/NotFound.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Story } from "@storybook/react"; 2 | 3 | import NotFound, { Props } from "./NotFound"; 4 | 5 | export default { 6 | title: "Components/Shared/NotFound", 7 | component: NotFound, 8 | }; 9 | 10 | const Template: Story = (args) => 깃들다; 11 | 12 | export const Default = Template.bind({}); 13 | Default.args = {}; 14 | -------------------------------------------------------------------------------- /frontend/src/components/@shared/PageError/PageError.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Story } from "@storybook/react"; 2 | 3 | import PageError, { Props } from "./PageError"; 4 | 5 | export default { 6 | title: "Components/Shared/PageError", 7 | component: PageError, 8 | }; 9 | 10 | const Template: Story = (args) => 깃들다; 11 | 12 | export const Default = Template.bind({}); 13 | Default.args = {}; 14 | -------------------------------------------------------------------------------- /frontend/src/components/@shared/PageError/PageError.tsx: -------------------------------------------------------------------------------- 1 | import { Container, ErrorImage, ErrorText } from "./PageError.style"; 2 | import CannotFindImage from "../../../assets/images/cannot-find.png"; 3 | 4 | export interface Props { 5 | errorMessage?: string; 6 | } 7 | 8 | const PageError = ({ errorMessage }: Props) => { 9 | return ( 10 | 11 | 12 | {errorMessage} 13 | 14 | ); 15 | }; 16 | 17 | export default PageError; 18 | -------------------------------------------------------------------------------- /frontend/src/components/@shared/ShareLink/ShareLink.style.tsx: -------------------------------------------------------------------------------- 1 | import styled, { css, CSSProp } from "styled-components"; 2 | 3 | export const Container = styled.div<{ cssProp?: CSSProp }>( 4 | ({ cssProp }) => css` 5 | ${cssProp} 6 | ` 7 | ); 8 | -------------------------------------------------------------------------------- /frontend/src/components/@shared/SliderHeader/SliderHeader.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Story } from "@storybook/react"; 2 | 3 | import SliderHeader, { Props } from "./SliderHeader"; 4 | 5 | export default { 6 | title: "Components/Shared/SliderHeader", 7 | component: SliderHeader, 8 | }; 9 | 10 | const Template: Story = (args) => ; 11 | 12 | export const Default = Template.bind({}); 13 | Default.args = {}; 14 | -------------------------------------------------------------------------------- /frontend/src/components/@shared/SliderHeader/SliderHeader.style.ts: -------------------------------------------------------------------------------- 1 | import styled, { css, CSSProp } from "styled-components"; 2 | import { LAYOUT } from "../../../constants/layout"; 3 | 4 | export const Container = styled.div<{ cssProp?: CSSProp }>( 5 | ({ cssProp }) => css` 6 | display: flex; 7 | justify-content: flex-end; 8 | align-items: center; 9 | width: 100%; 10 | min-height: ${LAYOUT.HEADER_HEIGHT}; 11 | padding: 1.0625rem 1.375rem; 12 | 13 | ${cssProp} 14 | ` 15 | ); 16 | 17 | export const CloseLinkButtonWrapper = styled.div` 18 | display: flex; 19 | cursor: pointer; 20 | transition: opacity 0.5s; 21 | 22 | :hover { 23 | opacity: 0.5; 24 | } 25 | `; 26 | 27 | export const CloseLinkButton = styled.a``; 28 | -------------------------------------------------------------------------------- /frontend/src/components/@shared/SliderHeader/SliderHeader.tsx: -------------------------------------------------------------------------------- 1 | import { CSSProp } from "styled-components"; 2 | import SVGIcon from "../SVGIcon/SVGIcon"; 3 | import { Container, CloseLinkButton, CloseLinkButtonWrapper } from "./SliderHeader.style"; 4 | 5 | export interface Props { 6 | onSlideDown?: () => void; 7 | cssProp?: CSSProp; 8 | } 9 | 10 | const SliderHeader = ({ onSlideDown, cssProp }: Props) => { 11 | return ( 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | ); 20 | }; 21 | 22 | export default SliderHeader; 23 | -------------------------------------------------------------------------------- /frontend/src/components/@shared/Tabs/Tabs.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Story } from "@storybook/react"; 2 | 3 | import Tabs, { Props } from "./Tabs"; 4 | 5 | export default { 6 | title: "Components/Shared/Tabs", 7 | component: Tabs, 8 | }; 9 | 10 | const Template: Story = (args) => ; 11 | 12 | export const Default = Template.bind({}); 13 | Default.args = { 14 | tabItems: [ 15 | { 16 | name: "포스트", 17 | onTabChange: () => {}, 18 | }, 19 | { 20 | name: "Github 통계", 21 | onTabChange: () => {}, 22 | }, 23 | ], 24 | }; 25 | -------------------------------------------------------------------------------- /frontend/src/components/@shared/TextEditor/TextEditor.style.ts: -------------------------------------------------------------------------------- 1 | import styled, { css, CSSProp } from "styled-components"; 2 | import { NoneStyledTextarea } from "../../@styled/layout"; 3 | import { customScrollbarCSS } from "../../@styled/scrollbar"; 4 | 5 | export const TextArea = styled(NoneStyledTextarea)<{ cssProp?: CSSProp; autoGrow: boolean }>( 6 | ({ theme, cssProp, autoGrow }) => css` 7 | ${autoGrow 8 | ? ` 9 | ::-webkit-scrollbar { 10 | width: 0px; 11 | } 12 | ` 13 | : customScrollbarCSS(theme.color.textColor)}; 14 | 15 | color: ${theme.color.textColor}; 16 | ${cssProp} 17 | ` 18 | ); 19 | -------------------------------------------------------------------------------- /frontend/src/components/@shared/ToggleButton/ToggleButton.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Story } from "@storybook/react"; 2 | 3 | import ToggleButton, { Props } from "./ToggleButton"; 4 | 5 | export default { 6 | title: "Components/Shared/ToggleButton", 7 | component: ToggleButton, 8 | }; 9 | 10 | const Template: Story = (args) => 깃들다; 11 | 12 | export const Default = Template.bind({}); 13 | Default.args = {}; 14 | -------------------------------------------------------------------------------- /frontend/src/components/@shared/ToggleButton/ToggleButton.tsx: -------------------------------------------------------------------------------- 1 | import { CSSProp } from "styled-components"; 2 | import { Container, Switch, Slider, Checkbox, ToggleButtonText } from "./ToggleButton.style"; 3 | 4 | export interface Props { 5 | toggleButtonText?: string; 6 | isToggled: boolean; 7 | onToggle: () => void; 8 | cssProp?: CSSProp; 9 | } 10 | 11 | const ToggleButton = ({ toggleButtonText, isToggled, cssProp, onToggle }: Props) => { 12 | return ( 13 | 14 | {toggleButtonText} 15 | 16 | 17 | 18 | 19 | 20 | ); 21 | }; 22 | 23 | export default ToggleButton; 24 | -------------------------------------------------------------------------------- /frontend/src/components/@styled/BackDrop.tsx: -------------------------------------------------------------------------------- 1 | import styled from "styled-components"; 2 | import { Z_INDEX } from "../../constants/layout"; 3 | 4 | const BackDrop = styled.div` 5 | position: fixed; 6 | z-index: ${({ zIndex }) => zIndex ?? Z_INDEX.UNDER_ROOT}; 7 | top: 0; 8 | left: 0; 9 | width: 100%; 10 | height: 100%; 11 | background-color: rgba(0, 0, 0, 0.5); 12 | `; 13 | 14 | export default BackDrop; 15 | -------------------------------------------------------------------------------- /frontend/src/components/Feed/Feed.style.ts: -------------------------------------------------------------------------------- 1 | import styled, { css } from "styled-components"; 2 | import { setMobileMediaQuery } from "../@styled/mediaQueries"; 3 | 4 | export const Container = styled.div``; 5 | 6 | export const PostItemWrapper = styled.div` 7 | position: relative; 8 | margin-bottom: 2rem; 9 | border-radius: 5px; 10 | background-color: ${({ theme }) => theme.color.white}; 11 | box-shadow: 1px 2px 6px rgba(0, 0, 0, 0.2); 12 | ${setMobileMediaQuery` 13 | box-shadow: none; 14 | `}; 15 | `; 16 | 17 | export const NotFoundCSS = css` 18 | margin-top: 3rem; 19 | width: 100%; 20 | `; 21 | -------------------------------------------------------------------------------- /frontend/src/components/GithubStatistics/GithubStatistics.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Story } from "@storybook/react"; 2 | 3 | import GithubStatistics, { Props } from "./GithubStatistics"; 4 | 5 | export default { 6 | title: "Components/GithubStatistics", 7 | component: GithubStatistics, 8 | }; 9 | 10 | const Template: Story = (args) => ; 11 | 12 | export const Default = Template.bind({}); 13 | Default.args = { 14 | username: "tanney-102", 15 | }; 16 | -------------------------------------------------------------------------------- /frontend/src/components/OneDepthStepHeader/OneDepthStepHeader.tsx: -------------------------------------------------------------------------------- 1 | import { useHistory } from "react-router-dom"; 2 | import StepHeader from "../@layout/StepHeader/StepHeader"; 3 | 4 | export interface Props { 5 | title: string; 6 | } 7 | 8 | const OneDepthStepHeader = ({ title }: Props) => { 9 | const history = useHistory(); 10 | 11 | return ( 12 | 13 | {title} 14 | 15 | ); 16 | }; 17 | 18 | export default OneDepthStepHeader; 19 | -------------------------------------------------------------------------------- /frontend/src/components/PortfolioContactForm/PortfolioContactForm.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Story } from "@storybook/react"; 2 | 3 | import PortfolioContactForm, { Props } from "./PortfolioContactForm"; 4 | 5 | export default { 6 | title: "Components/PortfolioContactForm", 7 | component: PortfolioContactForm, 8 | }; 9 | 10 | const Template: Story = (args) => 깃들다; 11 | 12 | export const Default = Template.bind({}); 13 | Default.args = {}; 14 | -------------------------------------------------------------------------------- /frontend/src/components/PortfolioProjectSection/PortfolioProjectSection.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Story } from "@storybook/react"; 2 | 3 | import PortfolioProjectSection, { Props } from "./PortfolioProjectSection"; 4 | 5 | export default { 6 | title: "Components/PortfolioProjectSection", 7 | component: PortfolioProjectSection, 8 | }; 9 | 10 | const Template: Story = (args) => 깃들다; 11 | 12 | export const Default = Template.bind({}); 13 | Default.args = {}; 14 | -------------------------------------------------------------------------------- /frontend/src/components/PortfolioSection/PortfolioSection.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Story } from "@storybook/react"; 2 | 3 | import PortfolioSection, { Props } from "./PortfolioSection"; 4 | 5 | export default { 6 | title: "Components/PortfolioSection", 7 | component: PortfolioSection, 8 | }; 9 | 10 | const Template: Story = (args) => 깃들다; 11 | 12 | export const Default = Template.bind({}); 13 | Default.args = {}; 14 | -------------------------------------------------------------------------------- /frontend/src/components/PortfolioTextEditor/PortfolioTextEditor.style.ts: -------------------------------------------------------------------------------- 1 | import styled, { css, CSSProp } from "styled-components"; 2 | import { NoneStyledTextarea } from "../@styled/layout"; 3 | 4 | export const Container = styled.div``; 5 | 6 | export const TextAreaCSS = css``; 7 | -------------------------------------------------------------------------------- /frontend/src/components/PortfolioTextEditor/PortfolioTextEditor.tsx: -------------------------------------------------------------------------------- 1 | import { css } from "styled-components"; 2 | import TextEditor, { TextEditorProps } from "../@shared/TextEditor/TextEditor"; 3 | import { TextAreaCSS } from "./PortfolioTextEditor.style"; 4 | 5 | export interface Props extends TextEditorProps {} 6 | 7 | const PortfolioTextEditor = ({ value, cssProp, placeholder, disabled, autoGrow, onChange }: Props) => { 8 | return ( 9 | 20 | ); 21 | }; 22 | 23 | export default PortfolioTextEditor; 24 | -------------------------------------------------------------------------------- /frontend/src/components/PostAddStepHeader/PostAddStepHeader.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Story } from "@storybook/react"; 2 | 3 | import PostAddStepHeader from "./PostAddStepHeader"; 4 | 5 | export default { 6 | title: "Components/PostAddStepHeader", 7 | component: PostAddStepHeader, 8 | }; 9 | 10 | const Template: Story = (args) => ; 11 | 12 | export const Default = Template.bind({}); 13 | Default.args = {}; 14 | -------------------------------------------------------------------------------- /frontend/src/components/PostAddStepHeader/PostAddStepHeader.style.tsx: -------------------------------------------------------------------------------- 1 | import styled from "styled-components"; -------------------------------------------------------------------------------- /frontend/src/components/PostAddStepHeader/PostAddStepHeader.tsx: -------------------------------------------------------------------------------- 1 | import { POST_ADD_STEPS } from "../../constants/steps"; 2 | import usePostAddStep from "../../hooks/service/usePostAddStep"; 3 | import StepHeader from "../@layout/StepHeader/StepHeader"; 4 | 5 | const PostAddStepHeader = () => { 6 | const { stepIndex, goBack, goNextStep } = usePostAddStep(POST_ADD_STEPS); 7 | 8 | return ( 9 | 10 | {POST_ADD_STEPS[stepIndex].title} 11 | 12 | ); 13 | }; 14 | 15 | export default PostAddStepHeader; 16 | -------------------------------------------------------------------------------- /frontend/src/components/PostContentUploader/PostContentUploader.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Story } from "@storybook/react"; 2 | 3 | import PostContentUploader from "./PostContentUploader"; 4 | 5 | export default { 6 | title: "Components/PostContentUploader", 7 | component: PostContentUploader, 8 | }; 9 | 10 | const Template: Story = (args) => ( 11 | {}} 15 | setFiles={() => {}} 16 | isImageUploaderShown={true} 17 | {...args} 18 | /> 19 | ); 20 | 21 | export const Default = Template.bind({}); 22 | Default.args = {}; 23 | -------------------------------------------------------------------------------- /frontend/src/components/PostSelector/PostSelector.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Story } from "@storybook/react"; 2 | 3 | import PostSelector, { Props } from "./PostSelector"; 4 | 5 | export default { 6 | title: "Components/PostSelector", 7 | component: PostSelector, 8 | }; 9 | 10 | const Template: Story = (args) => ; 11 | 12 | export const Default = Template.bind({}); 13 | Default.args = {}; 14 | -------------------------------------------------------------------------------- /frontend/src/components/PostTextEditor/PostTextEditor.style.ts: -------------------------------------------------------------------------------- 1 | import styled, { css, CSSProp } from "styled-components"; 2 | 3 | export const Container = styled.div<{ cssProp?: CSSProp }>( 4 | ({ cssProp }) => css` 5 | ${cssProp} 6 | display: flex; 7 | flex-direction: column; 8 | align-items: flex-end; 9 | ` 10 | ); 11 | 12 | export const TextLengthIndicator = styled.div( 13 | ({ theme }) => css` 14 | margin-top: 0.5rem; 15 | color: ${theme.color.lighterTextColor}; 16 | font-size: 0.8rem; 17 | float: right; 18 | 19 | display: flex; 20 | justify-content: flex-end; 21 | width: 3.5rem; 22 | height: 1rem; 23 | ` 24 | ); 25 | 26 | export const TextEditorCSS = css` 27 | width: 100%; 28 | height: 100%; 29 | `; 30 | -------------------------------------------------------------------------------- /frontend/src/components/Profile/Profile.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Story } from "@storybook/react"; 2 | 3 | import LoggedInWrapper from "../../../.storybook/utils/LoggedInWrapper"; 4 | import Profile, { Props } from "./Profile"; 5 | 6 | export default { 7 | title: "Components/Profile", 8 | component: Profile, 9 | }; 10 | 11 | const Template: Story = (args) => ( 12 | 13 | 14 | 15 | ); 16 | 17 | export const Default = Template.bind({}); 18 | Default.args = { 19 | isMyProfile: false, 20 | }; 21 | 22 | export const ProfileMe = Template.bind({}); 23 | ProfileMe.args = { 24 | isMyProfile: true, 25 | }; 26 | -------------------------------------------------------------------------------- /frontend/src/components/ProfileModificationForm/ProfileModificationForm.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Story } from "@storybook/react"; 2 | 3 | import ProfileModificationForm, { Props } from "./ProfileModificationForm"; 4 | 5 | export default { 6 | title: "Components/ProfileModificationForm", 7 | component: ProfileModificationForm, 8 | }; 9 | 10 | const Template: Story = (args) => ; 11 | 12 | export const Default = Template.bind({}); 13 | Default.args = { 14 | username: "tanney-102", 15 | profileImageUrl: 16 | "https://images.unsplash.com/photo-1518574095400-c75c9b094daa?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=934&q=80", 17 | prevDescription: "으하하하", 18 | }; 19 | -------------------------------------------------------------------------------- /frontend/src/components/ProfileTabContents/ProfileTabContents.stories.tsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woowacourse-teams/2021-pick-git/79b2c28bba8b72b30ef50c5aeecb904dcfba0c93/frontend/src/components/ProfileTabContents/ProfileTabContents.stories.tsx -------------------------------------------------------------------------------- /frontend/src/components/ProfileTabContents/ProfileTabContents.style.ts: -------------------------------------------------------------------------------- 1 | import { css } from "styled-components"; 2 | 3 | export const NotFoundCSS = css` 4 | margin: 3rem auto; 5 | width: 75%; 6 | `; 7 | -------------------------------------------------------------------------------- /frontend/src/components/RepositorySelector/RepositorySelector.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Story } from "@storybook/react"; 2 | 3 | import RepositorySelector from "./RepositorySelector"; 4 | 5 | export default { 6 | title: "Components/RepositorySelector", 7 | component: RepositorySelector, 8 | }; 9 | 10 | const Template: Story = (args) => ( 11 | {}} setGithubRepositoryName={() => {}} {...args} /> 12 | ); 13 | 14 | export const Default = Template.bind({}); 15 | Default.args = {}; 16 | -------------------------------------------------------------------------------- /frontend/src/components/TagInputForm/TagInputForm.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Story } from "@storybook/react"; 2 | 3 | import TagInputForm from "./TagInputForm"; 4 | 5 | export default { 6 | title: "Components/TagInputForm", 7 | component: TagInputForm, 8 | }; 9 | 10 | const Template: Story = (args) => {}} {...args} />; 11 | 12 | export const Default = Template.bind({}); 13 | Default.args = {}; 14 | -------------------------------------------------------------------------------- /frontend/src/constants/animation.ts: -------------------------------------------------------------------------------- 1 | export const LOGIN_ANIMATION = { 2 | MAX_DOT_COUNT: 3, 3 | DOT_COUNTING_INTERVAL: 500, 4 | }; 5 | -------------------------------------------------------------------------------- /frontend/src/constants/error.ts: -------------------------------------------------------------------------------- 1 | export const httpErrorStatus = { 2 | 400: "badRequest", 3 | 401: "unauthorized", 4 | 403: "forbidden", 5 | 404: "notFound", 6 | 405: "methodNotAllowed", 7 | 409: "conflict", 8 | 429: "tooManyRequests", 9 | 500: "serverError", 10 | } as const; 11 | 12 | export const httpErrorStatusName = Object.values(httpErrorStatus); 13 | 14 | export const clientErrorCodeMap = { 15 | C0001: "noAccessToken", 16 | C0002: "fileReader", 17 | } as const; 18 | 19 | export const clientErrorCodeName = Object.values(clientErrorCodeMap); 20 | -------------------------------------------------------------------------------- /frontend/src/constants/layout.ts: -------------------------------------------------------------------------------- 1 | export const LAYOUT = { 2 | HEADER_HEIGHT: "3.625rem", 3 | PAGE_MARGIN_TOP: "4.625rem", 4 | COMMENT_INPUT_HEIGHT: "3.625rem", 5 | CUSTOM_SCROLLBAR_WIDTH: "0.625rem", 6 | }; 7 | 8 | export const PAGE_WIDTH = { 9 | TABLET: "425px", 10 | LAPTOP: "640px", 11 | DESKTOP: "720px", 12 | }; 13 | 14 | export const LayoutInPx = { 15 | HEADER_HEIGHT: 58, 16 | PAGE_MARGIN_TOP: 74, 17 | }; 18 | 19 | export const Z_INDEX = { 20 | HIGHEST: 1200, 21 | HIGH: 900, 22 | MIDDLE: 600, 23 | MIDDLE_LOW: 450, 24 | LOW: 300, 25 | LOWER: 150, 26 | ROOT: 0, 27 | UNDER_ROOT: -1, 28 | BACKGROUND: -10, 29 | }; 30 | -------------------------------------------------------------------------------- /frontend/src/constants/limits.ts: -------------------------------------------------------------------------------- 1 | export const LIMIT = { 2 | COMMENT_LENGTH: 100, 3 | PROFILE_DESCRIPTION_LENGTH: 160, 4 | FEED_COUNT_PER_FETCH: 5, 5 | COMMENTS_COUNT_PER_FETCH: 10, 6 | REPOSITORIES_COUNT_PER_FETCH: 15, 7 | POST_LIKE_PERSON_COUNT_PER_FETCH: 10, 8 | SEARCH_RESULT_COUNT_PER_FETCH: 10, 9 | POST_CONTENT_HIDE_LENGTH: 200, 10 | POST_CONTENT_MAX_LENGTH: 500, 11 | POST_FILE_MAX_SIZE: 1000 * 1000 * 1, 12 | POST_FILE_MAX_COUNT: 5, 13 | POST_TAG_HIDE_LENGTH: 3, 14 | POST_TAG_LENGTH: 20, 15 | }; 16 | -------------------------------------------------------------------------------- /frontend/src/constants/localStorageKey.ts: -------------------------------------------------------------------------------- 1 | export const PORTFOLIO = { 2 | INTRO: (username: string) => `${username}-portfolioIntro`, 3 | PROJECTS: (username: string) => `${username}-portfolioProjects`, 4 | SECTIONS: (username: string) => `${username}-portfolioSections`, 5 | CONTACTS: (username: string) => `${username}-portfolioContacts`, 6 | }; 7 | -------------------------------------------------------------------------------- /frontend/src/constants/placeholder.ts: -------------------------------------------------------------------------------- 1 | export const PLACE_HOLDER = { 2 | CATEGORY: "소분류", 3 | DESCRIPTION: "설명", 4 | SECTION_NAME: "대분류", 5 | PROJECT_NAME: "프로젝트 이름", 6 | PROJECT_DESCRIPTION: "프로젝트 설명", 7 | INTRO_NAME: "이름", 8 | INTRO_DESCRIPTION: "소개", 9 | }; 10 | -------------------------------------------------------------------------------- /frontend/src/constants/snackbar.ts: -------------------------------------------------------------------------------- 1 | export const MAX_STACK_NUM = 3; 2 | export const SNACKBAR_HEIGHT_REM = 2.3; 3 | export const SNACKBAR_GAP_REM = 0.5; 4 | export const SNACKBAR_DURATION = 4000; 5 | 6 | export const snackBarOrders = Array.from({ length: MAX_STACK_NUM }, (_, index) => index + 1); 7 | export type SnackBarOrder = typeof snackBarOrders[number]; 8 | -------------------------------------------------------------------------------- /frontend/src/constants/steps.ts: -------------------------------------------------------------------------------- 1 | import { Step } from "../@types"; 2 | 3 | export const POST_ADD_STEPS: Step[] = [ 4 | { title: "Git 리포지터리", hash: "repository" }, 5 | { title: "이미지 & 글 작성", hash: "content" }, 6 | { title: "태그 입력", hash: "tags" }, 7 | ]; 8 | 9 | export const POST_EDIT_STEPS: Step[] = [ 10 | { title: "글 수정", hash: "content" }, 11 | { title: "태그 수정", hash: "tags" }, 12 | ]; 13 | 14 | export const COMMENT_SLIDE_STEPS: Step[] = [ 15 | { title: "사진/동영상", hash: "#files" }, 16 | { title: "작성글", hash: "#content" }, 17 | { title: "태그목록", hash: "#tags" }, 18 | ]; 19 | -------------------------------------------------------------------------------- /frontend/src/contexts/SearchContext.tsx: -------------------------------------------------------------------------------- 1 | import { createContext, Dispatch, SetStateAction, useState } from "react"; 2 | 3 | interface Props { 4 | children: React.ReactNode; 5 | } 6 | 7 | interface Value { 8 | keyword: string; 9 | setKeyword: Dispatch>; 10 | } 11 | 12 | const SearchContext = createContext({ 13 | keyword: "", 14 | setKeyword: () => {}, 15 | }); 16 | 17 | export const SearchContextProvider = ({ children }: Props) => { 18 | const [keyword, setKeyword] = useState(""); 19 | 20 | return {children}; 21 | }; 22 | 23 | export default SearchContext; 24 | -------------------------------------------------------------------------------- /frontend/src/hooks/common/useAuth.ts: -------------------------------------------------------------------------------- 1 | import { useContext } from "react"; 2 | 3 | import UserContext from "../../contexts/UserContext"; 4 | 5 | const useAuth = () => { 6 | const { currentUsername, isLoggedIn, login, logout } = useContext(UserContext); 7 | 8 | return { 9 | currentUsername, 10 | isLoggedIn, 11 | login, 12 | logout, 13 | }; 14 | }; 15 | 16 | export default useAuth; 17 | -------------------------------------------------------------------------------- /frontend/src/hooks/common/useDebounce.ts: -------------------------------------------------------------------------------- 1 | import { useRef } from "react"; 2 | 3 | const useDebounce = (callback: (arg?: TArg) => void, delay: number) => { 4 | const timer = useRef | null>(); 5 | 6 | const debounce = (arg?: TArg) => { 7 | if (timer.current) clearTimeout(timer.current); 8 | 9 | timer.current = setTimeout(() => callback(arg), delay); 10 | }; 11 | 12 | return debounce; 13 | }; 14 | 15 | export default useDebounce; 16 | -------------------------------------------------------------------------------- /frontend/src/hooks/common/useModal.ts: -------------------------------------------------------------------------------- 1 | import { useState } from "react"; 2 | 3 | const useModal = (initialState = false) => { 4 | const [modalMessage, setModalMessage] = useState(""); 5 | const [isModalShown, setIsModalShown] = useState(initialState); 6 | 7 | const showModal = (message?: string) => { 8 | message && setModalMessage(message); 9 | setIsModalShown(true); 10 | }; 11 | 12 | const hideModal = () => setIsModalShown(false); 13 | 14 | return { isModalShown, modalMessage, showModal, hideModal }; 15 | }; 16 | 17 | export default useModal; 18 | -------------------------------------------------------------------------------- /frontend/src/hooks/common/useSearchKeyword.ts: -------------------------------------------------------------------------------- 1 | import { useContext } from "react"; 2 | 3 | import SearchContext from "../../contexts/SearchContext"; 4 | 5 | const useSearchKeyword = () => { 6 | const { keyword, setKeyword } = useContext(SearchContext); 7 | 8 | const resetKeyword = () => { 9 | setKeyword(""); 10 | }; 11 | 12 | const changeKeyword = (newKeyword: string) => { 13 | setKeyword(newKeyword); 14 | }; 15 | 16 | return { keyword, resetKeyword, changeKeyword }; 17 | }; 18 | 19 | export default useSearchKeyword; 20 | -------------------------------------------------------------------------------- /frontend/src/hooks/common/useSnackbar.ts: -------------------------------------------------------------------------------- 1 | import { useContext } from "react"; 2 | 3 | import SnackBarContext from "../../contexts/SnackbarContext"; 4 | 5 | const useSnackbar = () => { 6 | const { pushSnackbarMessage } = useContext(SnackBarContext); 7 | 8 | return { pushSnackbarMessage }; 9 | }; 10 | 11 | export default useSnackbar; 12 | -------------------------------------------------------------------------------- /frontend/src/hooks/common/useThrottle.ts: -------------------------------------------------------------------------------- 1 | import { useRef } from "react"; 2 | 3 | const useThrottle = (callback: (...args: any[]) => void, delay: number) => { 4 | const timer = useRef | null>(null); 5 | 6 | return (...args: any[]) => { 7 | if (timer.current) return; 8 | 9 | timer.current = setTimeout(() => { 10 | timer.current = null; 11 | }, delay); 12 | 13 | callback(...args); 14 | }; 15 | }; 16 | 17 | export default useThrottle; 18 | -------------------------------------------------------------------------------- /frontend/src/hooks/service/useGithubTags.ts: -------------------------------------------------------------------------------- 1 | import { useGithubTagsQuery } from "../../services/queries"; 2 | 3 | const useGithubTags = (githubRepositoryName: string) => useGithubTagsQuery(githubRepositoryName); 4 | 5 | export default useGithubTags; 6 | -------------------------------------------------------------------------------- /frontend/src/hooks/service/usePortfolio.ts: -------------------------------------------------------------------------------- 1 | import { usePortfolioQuery, useSetPortfolioMutation } from "../../services/queries/portfolio"; 2 | 3 | const usePortfolio = (username: string, isMyPortfolio: boolean = false) => { 4 | const { data, isError, isLoading, error, isFetching, refetch } = usePortfolioQuery(username, isMyPortfolio); 5 | const { mutateAsync: mutateSetPortfolio } = useSetPortfolioMutation(); 6 | 7 | return { 8 | portfolio: data ?? null, 9 | isError, 10 | isLoading, 11 | error, 12 | isFetching, 13 | refetch, 14 | mutateSetPortfolio, 15 | }; 16 | }; 17 | 18 | export default usePortfolio; 19 | -------------------------------------------------------------------------------- /frontend/src/hooks/service/usePostDetail.ts: -------------------------------------------------------------------------------- 1 | import { useGetPostQuery } from "../../services/queries"; 2 | 3 | const usePostDetail = (postId: number, activated: boolean = false) => { 4 | const { data: post, isLoading, isError } = useGetPostQuery(postId, activated); 5 | 6 | return { 7 | post, 8 | isLoading, 9 | isError, 10 | }; 11 | }; 12 | 13 | export default usePostDetail; 14 | -------------------------------------------------------------------------------- /frontend/src/hooks/service/usePostLikePeople.ts: -------------------------------------------------------------------------------- 1 | import { Post } from "../../@types"; 2 | import { usePostLikePeopleQuery } from "../../services/queries/postLikePeople"; 3 | 4 | const usePostLikePeople = (postId: Post["id"]) => { 5 | const { data: postLikePeople, isError, isLoading, refetch } = usePostLikePeopleQuery(postId); 6 | 7 | return { 8 | postLikePeople, 9 | isError, 10 | isLoading, 11 | refetch, 12 | }; 13 | }; 14 | 15 | export default usePostLikePeople; 16 | -------------------------------------------------------------------------------- /frontend/src/mocks/routes.json: -------------------------------------------------------------------------------- 1 | { 2 | "/api/*": "/$1", 3 | "/profiles/me": "/self-profiles", 4 | "/profiles/:username": "/user-profiles", 5 | "/authorization/github": "/github-login", 6 | "/posts?page=:page&limit=:limit": "/posts?_page=:page&_limit=:limit", 7 | "/posts/:username": "/posts", 8 | "/search/posts?type=tags&keyword=asd&page=0&limit=5": "/posts", 9 | "/search/posts?type=tags&keyword=asd&page=1&limit=5": "/empty" 10 | } 11 | -------------------------------------------------------------------------------- /frontend/src/pages/AddPostPage/AddPostPage.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Story } from "@storybook/react"; 2 | 3 | import AddPostPage from "./AddPostPage"; 4 | 5 | export default { 6 | title: "Pages/AddPostPage", 7 | component: AddPostPage, 8 | }; 9 | 10 | const Template: Story = (args) => ; 11 | 12 | export const Default = Template.bind({}); 13 | Default.args = {}; 14 | -------------------------------------------------------------------------------- /frontend/src/pages/AuthLoginProcessingPage/AuthLoginProcessingPage.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Story } from "@storybook/react"; 2 | 3 | import AuthLoginProcessingPage from "./AuthLoginProcessingPage"; 4 | 5 | export default { 6 | title: "Pages/AuthLoginProcessingPage", 7 | component: AuthLoginProcessingPage, 8 | }; 9 | 10 | const Template: Story = () => ; 11 | 12 | export const Default = Template.bind({}); 13 | Default.args = {}; 14 | -------------------------------------------------------------------------------- /frontend/src/pages/CommentsPage/CommentsPage.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Story } from "@storybook/react"; 2 | 3 | import CommentsPage from "./CommentsPage"; 4 | 5 | export default { 6 | title: "Pages/CommentsPage", 7 | component: CommentsPage, 8 | }; 9 | 10 | const Template: Story = (args) => ; 11 | 12 | export const Default = Template.bind({}); 13 | Default.args = {}; 14 | -------------------------------------------------------------------------------- /frontend/src/pages/EditPostPage/EditPostPage.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Story } from "@storybook/react"; 2 | 3 | import EditPostPage from "./EditPostPage"; 4 | 5 | export default { 6 | title: "Pages/EditPostPage", 7 | component: EditPostPage, 8 | }; 9 | 10 | const Template: Story = (args) => ; 11 | 12 | export const Default = Template.bind({}); 13 | Default.args = {}; 14 | -------------------------------------------------------------------------------- /frontend/src/pages/FollowerListPage/FollowerListPage.style.ts: -------------------------------------------------------------------------------- 1 | import styled from "styled-components"; 2 | import { Page } from "../../components/@styled/layout"; 3 | 4 | export const Container = styled(Page)` 5 | background-color: ${({ theme }) => theme.color.white}; 6 | height: 100vh; 7 | `; 8 | 9 | export const ContentWrapper = styled.div` 10 | padding: 1.4375rem; 11 | `; 12 | -------------------------------------------------------------------------------- /frontend/src/pages/FollowingListPage/FollowingListPage.style.ts: -------------------------------------------------------------------------------- 1 | import styled from "styled-components"; 2 | import { Page } from "../../components/@styled/layout"; 3 | 4 | export const Container = styled(Page)` 5 | background-color: ${({ theme }) => theme.color.white}; 6 | `; 7 | 8 | export const ContentWrapper = styled.div` 9 | padding: 1.4375rem; 10 | `; 11 | -------------------------------------------------------------------------------- /frontend/src/pages/HomeFeedPage/HomeFeedPage.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Story } from "@storybook/react"; 2 | 3 | import HomeFeedPage from "./HomeFeedPage"; 4 | 5 | export default { 6 | title: "Pages/HomeFeedPage", 7 | component: HomeFeedPage, 8 | }; 9 | 10 | const Template: Story = (args) => ; 11 | 12 | export const Default = Template.bind({}); 13 | Default.args = {}; 14 | -------------------------------------------------------------------------------- /frontend/src/pages/HomeFeedPage/HomeFeedPage.style.ts: -------------------------------------------------------------------------------- 1 | import styled, { css } from "styled-components"; 2 | import { Page } from "../../components/@styled/layout"; 3 | import { setMobileMediaQuery, setTabletMediaQuery } from "../../components/@styled/mediaQueries"; 4 | 5 | export const Container = styled(Page)``; 6 | 7 | export const PostTabWrapper = styled.div` 8 | display: flex; 9 | justify-content: flex-end; 10 | margin-bottom: 1.25rem; 11 | `; 12 | 13 | export const postTabCSS = css` 14 | width: 40%; 15 | min-width: 10.625rem; 16 | 17 | ${setMobileMediaQuery` 18 | font-size: 0.8rem; 19 | `} 20 | 21 | ${setTabletMediaQuery` 22 | font-size:0.8rem; 23 | `}; 24 | `; 25 | -------------------------------------------------------------------------------- /frontend/src/pages/LoginPage/LoginPage.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Story } from "@storybook/react"; 2 | 3 | import LoginPage from "./LoginPage"; 4 | 5 | export default { 6 | title: "Pages/LoginPage", 7 | component: LoginPage, 8 | }; 9 | 10 | const Template: Story = () => ; 11 | 12 | export const Default = Template.bind({}); 13 | Default.args = {}; 14 | -------------------------------------------------------------------------------- /frontend/src/pages/PostLikePeoplePage/PostLikePeoplePage.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Story } from "@storybook/react"; 2 | 3 | import PostLikePeoplePage from "./PostLikePeoplePage"; 4 | 5 | export default { 6 | title: "Pages/PostLikePeoplePage", 7 | component: PostLikePeoplePage, 8 | }; 9 | 10 | const Template: Story = (args) => ; 11 | 12 | export const Default = Template.bind({}); 13 | Default.args = {}; 14 | -------------------------------------------------------------------------------- /frontend/src/pages/PostLikePeoplePage/PostLikePeoplePage.style.ts: -------------------------------------------------------------------------------- 1 | import styled from "styled-components"; 2 | import { Page } from "../../components/@styled/layout"; 3 | 4 | export const Container = styled(Page)` 5 | background-color: ${({ theme }) => theme.color.white}; 6 | `; 7 | 8 | export const ContentWrapper = styled.div` 9 | padding: 1.4375rem; 10 | `; 11 | -------------------------------------------------------------------------------- /frontend/src/pages/ProfilePage/ProfilePage.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Story } from "@storybook/react"; 2 | 3 | import ProfilePage, { Props } from "./ProfilePage"; 4 | 5 | export default { 6 | title: "Pages/ProfilePage", 7 | component: ProfilePage, 8 | }; 9 | 10 | const Template: Story = (args) => ; 11 | 12 | export const MyProfile = Template.bind({}); 13 | MyProfile.args = { 14 | isMyProfile: true, 15 | }; 16 | 17 | export const UserProfile = Template.bind({}); 18 | UserProfile.args = { 19 | isMyProfile: false, 20 | }; 21 | -------------------------------------------------------------------------------- /frontend/src/pages/ProfilePage/ProfilePage.style.ts: -------------------------------------------------------------------------------- 1 | import styled from "styled-components"; 2 | import { Page } from "../../components/@styled/layout"; 3 | 4 | export const Container = styled(Page)` 5 | background-color: ${({ theme }) => theme.color.white}; 6 | height: fit-content; 7 | min-height: 100vh; 8 | overflow-y: auto; 9 | `; 10 | 11 | export const LoadingWrapper = styled.div` 12 | height: 100vh; 13 | display: flex; 14 | justify-content: center; 15 | align-items: center; 16 | `; 17 | -------------------------------------------------------------------------------- /frontend/src/pages/SearchPage/SearchPage.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Story } from "@storybook/react"; 2 | 3 | import SearchPage from "./SearchPage"; 4 | 5 | export default { 6 | title: "Pages/SearchPage", 7 | component: SearchPage, 8 | }; 9 | 10 | const Template: Story = (args) => ; 11 | 12 | export const Default = Template.bind({}); 13 | Default.args = {}; 14 | -------------------------------------------------------------------------------- /frontend/src/pages/SearchPostResultPage/SearchPostResultPage.style.ts: -------------------------------------------------------------------------------- 1 | import styled from "styled-components"; 2 | import { Page } from "../../components/@styled/layout"; 3 | 4 | export const Container = styled(Page)``; 5 | -------------------------------------------------------------------------------- /frontend/src/pages/UserFeedPage/UserFeedPage.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Story } from "@storybook/react"; 2 | 3 | import UserFeedPage from "./UserFeedPage"; 4 | 5 | export default { 6 | title: "Pages/UserFeedPage", 7 | component: UserFeedPage, 8 | }; 9 | 10 | const Template: Story = (args) => ; 11 | 12 | export const Default = Template.bind({}); 13 | Default.args = {}; 14 | -------------------------------------------------------------------------------- /frontend/src/pages/UserFeedPage/UserFeedPage.style.ts: -------------------------------------------------------------------------------- 1 | import styled from "styled-components"; 2 | import { Page } from "../../components/@styled/layout"; 3 | 4 | export const Container = styled(Page)``; 5 | -------------------------------------------------------------------------------- /frontend/src/services/queries/githubStats.ts: -------------------------------------------------------------------------------- 1 | import { AxiosError } from "axios"; 2 | import { useQuery } from "react-query"; 3 | import { ErrorResponse, GithubStats } from "../../@types"; 4 | import { QUERY } from "../../constants/queries"; 5 | import { getAccessToken } from "../../storage/storage"; 6 | import { requestGetGithubStats } from "../requests"; 7 | 8 | export const useGithubStatsQuery = (username: string, activated: boolean) => { 9 | return useQuery>( 10 | [QUERY.GET_GITHUB_STATS, username], 11 | () => (activated ? requestGetGithubStats(username, getAccessToken()) : Promise.resolve(null)), 12 | { suspense: true } 13 | ); 14 | }; 15 | -------------------------------------------------------------------------------- /frontend/src/services/queries/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./profile"; 2 | export * from "./posts"; 3 | export * from "./comments"; 4 | export * from "./githubStats"; 5 | export * from "./github"; 6 | -------------------------------------------------------------------------------- /frontend/src/services/queries/postLikePeople.ts: -------------------------------------------------------------------------------- 1 | import { AxiosError } from "axios"; 2 | import { useQuery } from "react-query"; 3 | import { ErrorResponse, Post, UserItem } from "../../@types"; 4 | import { QUERY } from "../../constants/queries"; 5 | import { getAccessToken } from "../../storage/storage"; 6 | import { requestGetPostLikePeople } from "../requests/postLikePeople"; 7 | 8 | export const usePostLikePeopleQuery = (postId: Post["id"]) => { 9 | return useQuery, UserItem[], [string, number]>( 10 | [QUERY.GET_POST_LIKE_PEOPLE, postId], 11 | async ({ queryKey }) => { 12 | const [, postIdParam] = queryKey; 13 | return requestGetPostLikePeople(postIdParam, getAccessToken()); 14 | } 15 | ); 16 | }; 17 | -------------------------------------------------------------------------------- /frontend/src/services/requests/account.ts: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | import { API_URL } from "../../constants/urls"; 3 | 4 | export const requestGetGithubAuthLink = async () => { 5 | const response = await axios.get<{ url: string }>(API_URL.AUTH.GITHUB); 6 | 7 | return response.data.url; 8 | }; 9 | 10 | export const requestGetAccessToken = async (authCode: string) => { 11 | const response = await axios.get<{ username: string; token: string }>(API_URL.AFTER_LOGIN(authCode)); 12 | 13 | return response.data; 14 | }; 15 | -------------------------------------------------------------------------------- /frontend/src/services/requests/githubStats.ts: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | import { GithubStats } from "../../@types"; 3 | import { API_URL } from "../../constants/urls"; 4 | 5 | export const requestGetGithubStats = async (username: string, accessToken: string | null) => { 6 | if (!accessToken) { 7 | return null; 8 | } 9 | 10 | const response = await axios.get(API_URL.GITHUB_STATS(username), { 11 | headers: { 12 | Authorization: `Bearer ${accessToken}`, 13 | }, 14 | }); 15 | 16 | return response.data; 17 | }; 18 | -------------------------------------------------------------------------------- /frontend/src/services/requests/index.ts: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | 3 | switch (process.env.DEPLOY) { 4 | case "main": 5 | axios.defaults.baseURL = "https://api.pick-git.com/api"; 6 | break; 7 | case "develop": 8 | axios.defaults.baseURL = "http://devapi.pick-git.com:8080/api"; 9 | break; 10 | default: 11 | axios.defaults.baseURL = "http://devapi.pick-git.com:8080/api"; 12 | } 13 | 14 | export * from "./profile"; 15 | export * from "./account"; 16 | export * from "./posts"; 17 | export * from "./githubStats"; 18 | export * from "./github"; 19 | export * from "./search"; 20 | -------------------------------------------------------------------------------- /frontend/src/services/requests/postLikePeople.ts: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | import { Post, UserItem } from "../../@types"; 3 | import { API_URL } from "../../constants/urls"; 4 | 5 | export const requestGetPostLikePeople = async (postId: Post["id"], accessToken: string | null) => { 6 | const config = accessToken 7 | ? { 8 | headers: { 9 | Authorization: `Bearer ${accessToken}`, 10 | }, 11 | } 12 | : {}; 13 | 14 | const response = await axios.get(API_URL.POST_LIKE_PEOPLE(postId), config); 15 | 16 | return response.data; 17 | }; 18 | -------------------------------------------------------------------------------- /frontend/src/utils/data.ts: -------------------------------------------------------------------------------- 1 | export const removeDuplicatedData = (data: TData[] | null, uniqueKeyGenerator: (data: TData) => unknown) => { 2 | const uniqueSet = new Set(); 3 | 4 | return ( 5 | data?.filter((currentData) => { 6 | const uniqueKey = uniqueKeyGenerator(currentData); 7 | const isNewPost = !uniqueSet.has(uniqueKey); 8 | 9 | if (isNewPost) { 10 | uniqueSet.add(uniqueKey); 11 | } 12 | 13 | return isNewPost; 14 | }) ?? [] 15 | ); 16 | }; 17 | -------------------------------------------------------------------------------- /frontend/src/utils/history.ts: -------------------------------------------------------------------------------- 1 | export const getLastPath = (pathname: string) => { 2 | const [lastPath] = pathname.split("/").slice(-1); 3 | 4 | return lastPath; 5 | }; 6 | 7 | export const getLastHash = (pathname: string) => { 8 | const [lastHash] = pathname.split("#").slice(-1); 9 | 10 | return lastHash; 11 | }; 12 | -------------------------------------------------------------------------------- /frontend/src/utils/kakao.ts: -------------------------------------------------------------------------------- 1 | Kakao.init(process.env.KAKAO_API_KEY as string); 2 | 3 | export const sendKakaoShareLink = (template: Object) => { 4 | Kakao.Link.sendDefault(template); 5 | }; 6 | -------------------------------------------------------------------------------- /frontend/src/utils/layout.ts: -------------------------------------------------------------------------------- 1 | export const getScrollYPosition = (element: Element, container: Element) => { 2 | return element.getBoundingClientRect().top + container.scrollTop; 3 | }; 4 | -------------------------------------------------------------------------------- /frontend/src/utils/portfolio.ts: -------------------------------------------------------------------------------- 1 | import { TEMP_ID_INDICATOR } from "../constants/portfolio"; 2 | 3 | export const getTemporaryId = (weight?: number) => `${TEMP_ID_INDICATOR}${new Date().getTime() + (weight ?? 0)}`; 4 | 5 | export const isTempId = (id: string | number): id is string => { 6 | return typeof id === "string" && id.includes(TEMP_ID_INDICATOR); 7 | }; 8 | -------------------------------------------------------------------------------- /frontend/src/utils/preloaders.ts: -------------------------------------------------------------------------------- 1 | export const getImagePreloadPromise = (imageUrl: string) => 2 | new Promise((resolve, reject) => { 3 | const image = new Image(); 4 | 5 | image.src = imageUrl; 6 | image.onload = () => resolve(image); 7 | image.onerror = reject; 8 | }); 9 | 10 | export const getImagePreloadPromises = (imageUrls: string[]) => { 11 | return imageUrls.map(getImagePreloadPromise); 12 | }; 13 | -------------------------------------------------------------------------------- /frontend/src/utils/profileModification.ts: -------------------------------------------------------------------------------- 1 | import { LIMIT } from "../constants/limits"; 2 | 3 | export const isValidFileSize = (file: File) => { 4 | return file.size <= LIMIT.POST_FILE_MAX_SIZE; 5 | }; 6 | 7 | export const isValidProfileDescription = (description: string) => { 8 | return description.length <= LIMIT.PROFILE_DESCRIPTION_LENGTH; 9 | }; 10 | -------------------------------------------------------------------------------- /frontend/src/utils/tabs.ts: -------------------------------------------------------------------------------- 1 | import { TabIndicatorKind } from "../@types"; 2 | import { theme } from "../App.style"; 3 | 4 | export const getTabTextColor = (tabIndicatorKind: TabIndicatorKind, isFocused: boolean) => { 5 | if (tabIndicatorKind === "line") { 6 | return isFocused ? theme.color.textColor : theme.color.lighterTextColor; 7 | } 8 | 9 | return isFocused ? theme.color.white : theme.color.textColor; 10 | }; 11 | -------------------------------------------------------------------------------- /frontend/src/utils/text.tsx: -------------------------------------------------------------------------------- 1 | export const getTextElementsWithBr = (text: string) => { 2 | return text.split("\n").map((textLine, index) => ( 3 | 4 | {textLine} 5 |
6 |
7 | )); 8 | }; 9 | -------------------------------------------------------------------------------- /frontend/src/utils/typeGuard.ts: -------------------------------------------------------------------------------- 1 | import { InfiniteData } from "react-query"; 2 | import { ClientErrorCode, HTTPErrorStatus } from "../@types"; 3 | import { httpErrorStatus } from "../constants/error"; 4 | import { CLIENT_ERROR_MESSAGE } from "../constants/messages"; 5 | 6 | export const isClientErrorCode = (errorCode: string): errorCode is ClientErrorCode => errorCode in CLIENT_ERROR_MESSAGE; 7 | 8 | export const isHttpErrorStatus = (status: number): status is HTTPErrorStatus => status in httpErrorStatus; 9 | 10 | export const isInfiniteData = (data: InfiniteData | TData): data is InfiniteData => 11 | "pages" in data; 12 | -------------------------------------------------------------------------------- /frontend/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "jsx": "react", 4 | "target": "ES2015", 5 | "module": "esnext", 6 | "moduleResolution": "node", 7 | "strict": true, 8 | "esModuleInterop": true, 9 | "skipLibCheck": true, 10 | "forceConsistentCasingInFileNames": true, 11 | "allowUmdGlobalAccess": true 12 | }, 13 | "exclude": ["./node_modules"], 14 | "include": ["src/**/*"] 15 | } 16 | -------------------------------------------------------------------------------- /s3-proxy/README.md: -------------------------------------------------------------------------------- 1 | #S3-proxy 2 | -------------------------------------------------------------------------------- /s3-proxy/s3-proxy/.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | .gradle 3 | build/ 4 | !gradle/wrapper/gradle-wrapper.jar 5 | !**/src/main/**/build/ 6 | !**/src/test/**/build/ 7 | 8 | ### STS ### 9 | .apt_generated 10 | .classpath 11 | .factorypath 12 | .project 13 | .settings 14 | .springBeans 15 | .sts4-cache 16 | bin/ 17 | !**/src/main/**/bin/ 18 | !**/src/test/**/bin/ 19 | 20 | ### IntelliJ IDEA ### 21 | .idea 22 | *.iws 23 | *.iml 24 | *.ipr 25 | out/ 26 | !**/src/main/**/out/ 27 | !**/src/test/**/out/ 28 | 29 | ### NetBeans ### 30 | /nbproject/private/ 31 | /nbbuild/ 32 | /dist/ 33 | /nbdist/ 34 | /.nb-gradle/ 35 | 36 | ### VS Code ### 37 | .vscode/ 38 | -------------------------------------------------------------------------------- /s3-proxy/s3-proxy/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woowacourse-teams/2021-pick-git/79b2c28bba8b72b30ef50c5aeecb904dcfba0c93/s3-proxy/s3-proxy/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /s3-proxy/s3-proxy/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /s3-proxy/s3-proxy/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 's3-proxy' 2 | -------------------------------------------------------------------------------- /s3-proxy/s3-proxy/src/main/java/com/woowacourse/s3_proxy/S3ProxyApplication.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.s3_proxy; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class S3ProxyApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(S3ProxyApplication.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /s3-proxy/s3-proxy/src/main/java/com/woowacourse/s3_proxy/common/file_validator/FileValidator.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.s3_proxy.common.file_validator; 2 | 3 | import org.springframework.web.multipart.MultipartFile; 4 | 5 | public interface FileValidator { 6 | 7 | void execute(MultipartFile multipartFile); 8 | } 9 | -------------------------------------------------------------------------------- /s3-proxy/s3-proxy/src/main/java/com/woowacourse/s3_proxy/config/FileValidatorConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.s3_proxy.config; 2 | 3 | import com.woowacourse.s3_proxy.common.file_validator.FileValidator; 4 | import com.woowacourse.s3_proxy.common.file_validator.ImageFileValidator; 5 | import java.util.List; 6 | import org.springframework.context.annotation.Bean; 7 | import org.springframework.context.annotation.Configuration; 8 | 9 | @Configuration 10 | public class FileValidatorConfiguration { 11 | 12 | @Bean 13 | public List fileValidator() { 14 | return List.of( 15 | new ImageFileValidator() 16 | ); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /s3-proxy/s3-proxy/src/main/java/com/woowacourse/s3_proxy/exception/format/FileExtensionException.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.s3_proxy.exception.format; 2 | 3 | import com.woowacourse.s3_proxy.exception.ApplicationException; 4 | import org.springframework.http.HttpStatus; 5 | 6 | public class FileExtensionException extends ApplicationException { 7 | 8 | private static final String ERROR_CODE = "I0002"; 9 | private static final HttpStatus HTTP_STATUS = HttpStatus.BAD_REQUEST; 10 | 11 | public FileExtensionException(String message) { 12 | super(ERROR_CODE, HTTP_STATUS, message); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /s3-proxy/s3-proxy/src/main/java/com/woowacourse/s3_proxy/exception/format/HashFailureException.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.s3_proxy.exception.format; 2 | 3 | import com.woowacourse.s3_proxy.exception.ApplicationException; 4 | import org.springframework.http.HttpStatus; 5 | 6 | public class HashFailureException extends ApplicationException { 7 | 8 | private static final String ERROR_CODE = "I0003"; 9 | private static final HttpStatus HTTP_STATUS = HttpStatus.BAD_REQUEST; 10 | 11 | public HashFailureException(String message) { 12 | super(ERROR_CODE, HTTP_STATUS, message); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /s3-proxy/s3-proxy/src/main/java/com/woowacourse/s3_proxy/exception/upload/UploadFailureException.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.s3_proxy.exception.upload; 2 | 3 | import com.woowacourse.s3_proxy.exception.ApplicationException; 4 | import org.springframework.http.HttpStatus; 5 | 6 | public class UploadFailureException extends ApplicationException { 7 | 8 | private static final String ERROR_CODE = "I0001"; 9 | private static final HttpStatus HTTP_STATUS = HttpStatus.BAD_REQUEST; 10 | private static final String MESSAGE = "업로드 실패"; 11 | 12 | public UploadFailureException() { 13 | super(ERROR_CODE, HTTP_STATUS, MESSAGE); 14 | } 15 | 16 | public UploadFailureException(Exception e) { 17 | super(ERROR_CODE, HTTP_STATUS, MESSAGE, e); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /s3-proxy/s3-proxy/src/main/java/com/woowacourse/s3_proxy/web/presentation/resolver/ExtensionValid.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.s3_proxy.web.presentation.resolver; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | @Target(ElementType.PARAMETER) 9 | @Retention(RetentionPolicy.RUNTIME) 10 | public @interface ExtensionValid { 11 | 12 | } 13 | -------------------------------------------------------------------------------- /s3-proxy/s3-proxy/src/main/resources/application-test.yml: -------------------------------------------------------------------------------- 1 | security: 2 | public_keys: 3 | pick_git: ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCqLIG/jZ9jAmEZUzrOBkoF8/K+og4DIhAJ4MpWa0BYRtLjbvce1YXqlvUjHj7AcQgw/p0vc1JvzIJyEZIxLFG/3+WC3kv2RWZywUv2BDAApGjSocozeYaQIax/4sIFWXKqUASrvmtZOyV+fKZU4Nmxxhrfa8B1KWw0fsuaV01btVKLslC+es+dFvk/r4AbAeGQR6nk0JmeKeJFEgnXy9uNkJ5trjWGwYjXF46DWG0AnCxcLciPTZik4vUOG4e4KfAA2LVVgHGo0V/qtZuT6myaOFzRazetmvBKQt2B0yHZPRjLDNBZfWXV2refn60N65yJEc8Hv7OgnhhCZp6c04Tesj9RxLIzF8ItqwNj4YftuDH73al7VmYNMRPIowwD+X7lsZqRNRQxZEHYeropIxYEwHCNLbirPpdAoHtLAXhisAG6mQO4if6f0GESYvzGoTcrlfDbl1nF/Mk5Wh8QaRLYrFHxNltJVBnkQoakZiJKtxlNVtbLU20/HedIaRkwWSU= bperhaps@sonminscBookAir 4 | 5 | aws: 6 | cloud_front: 7 | file_url_format: https://djgd6o993rakk.cloudfront.net/images/%s 8 | s3: 9 | bucket_name: pick-git-datas 10 | -------------------------------------------------------------------------------- /s3-proxy/s3-proxy/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | security: 2 | public_keys: 3 | pick_git: ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCqLIG/jZ9jAmEZUzrOBkoF8/K+og4DIhAJ4MpWa0BYRtLjbvce1YXqlvUjHj7AcQgw/p0vc1JvzIJyEZIxLFG/3+WC3kv2RWZywUv2BDAApGjSocozeYaQIax/4sIFWXKqUASrvmtZOyV+fKZU4Nmxxhrfa8B1KWw0fsuaV01btVKLslC+es+dFvk/r4AbAeGQR6nk0JmeKeJFEgnXy9uNkJ5trjWGwYjXF46DWG0AnCxcLciPTZik4vUOG4e4KfAA2LVVgHGo0V/qtZuT6myaOFzRazetmvBKQt2B0yHZPRjLDNBZfWXV2refn60N65yJEc8Hv7OgnhhCZp6c04Tesj9RxLIzF8ItqwNj4YftuDH73al7VmYNMRPIowwD+X7lsZqRNRQxZEHYeropIxYEwHCNLbirPpdAoHtLAXhisAG6mQO4if6f0GESYvzGoTcrlfDbl1nF/Mk5Wh8QaRLYrFHxNltJVBnkQoakZiJKtxlNVtbLU20/HedIaRkwWSU= bperhaps@sonminscBookAir 4 | 5 | aws: 6 | cloud_front: 7 | file_url_format: https://djgd6o993rakk.cloudfront.net/images/%s 8 | s3: 9 | bucket_name: pick-git-datas/images 10 | -------------------------------------------------------------------------------- /s3-proxy/s3-proxy/src/main/resources/logback-access.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /s3-proxy/s3-proxy/src/main/resources/logback/api-logger.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /s3-proxy/s3-proxy/src/main/resources/logback/test/test-access-logger.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | utf8 6 | 7 | %n###### HTTP Request ###### %n%fullRequest###### HTTP Response ###### %n%fullResponse 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /s3-proxy/s3-proxy/src/main/resources/logback/test/test-basic-logger.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | utf8 6 | 7 | %d{yyyy-MM-dd HH:mm:ss.SSS} %-5level %t %class{36}.%M L:%L %n > %m%n 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /s3-proxy/s3-proxy/src/main/resources/s3proxy.pub: -------------------------------------------------------------------------------- 1 | ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCqLIG/jZ9jAmEZUzrOBkoF8/K+og4DIhAJ4MpWa0BYRtLjbvce1YXqlvUjHj7AcQgw/p0vc1JvzIJyEZIxLFG/3+WC3kv2RWZywUv2BDAApGjSocozeYaQIax/4sIFWXKqUASrvmtZOyV+fKZU4Nmxxhrfa8B1KWw0fsuaV01btVKLslC+es+dFvk/r4AbAeGQR6nk0JmeKeJFEgnXy9uNkJ5trjWGwYjXF46DWG0AnCxcLciPTZik4vUOG4e4KfAA2LVVgHGo0V/qtZuT6myaOFzRazetmvBKQt2B0yHZPRjLDNBZfWXV2refn60N65yJEc8Hv7OgnhhCZp6c04Tesj9RxLIzF8ItqwNj4YftuDH73al7VmYNMRPIowwD+X7lsZqRNRQxZEHYeropIxYEwHCNLbirPpdAoHtLAXhisAG6mQO4if6f0GESYvzGoTcrlfDbl1nF/Mk5Wh8QaRLYrFHxNltJVBnkQoakZiJKtxlNVtbLU20/HedIaRkwWSU= bperhaps@sonminscBookAir 2 | -------------------------------------------------------------------------------- /s3-proxy/s3-proxy/src/test/java/com/woowacourse/s3_proxy/S3ProxyApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.s3_proxy; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class S3ProxyApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /s3-proxy/s3-proxy/src/test/java/com/woowacourse/s3_proxy/config/StorageTestConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.woowacourse.s3_proxy.config; 2 | 3 | import cloud.localstack.awssdkv1.TestUtils; 4 | import com.amazonaws.services.s3.AmazonS3; 5 | import org.springframework.beans.factory.annotation.Value; 6 | import org.springframework.boot.test.context.TestConfiguration; 7 | import org.springframework.context.annotation.Bean; 8 | 9 | @TestConfiguration 10 | public class StorageTestConfiguration { 11 | 12 | @Value("${aws.s3.bucket_name}") 13 | private String bucket; 14 | 15 | @Bean 16 | public AmazonS3 amazonS3() { 17 | AmazonS3 amazonS3 = TestUtils.getClientS3(); 18 | amazonS3.createBucket(bucket); 19 | 20 | return amazonS3; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /s3-proxy/s3-proxy/src/test/resources/testFailData.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | -------------------------------------------------------------------------------- /s3-proxy/s3-proxy/src/test/resources/testFailImage1.jpg: -------------------------------------------------------------------------------- 1 | ddd -------------------------------------------------------------------------------- /s3-proxy/s3-proxy/src/test/resources/testFailImage2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woowacourse-teams/2021-pick-git/79b2c28bba8b72b30ef50c5aeecb904dcfba0c93/s3-proxy/s3-proxy/src/test/resources/testFailImage2.jpg -------------------------------------------------------------------------------- /s3-proxy/s3-proxy/src/test/resources/testRightImage1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woowacourse-teams/2021-pick-git/79b2c28bba8b72b30ef50c5aeecb904dcfba0c93/s3-proxy/s3-proxy/src/test/resources/testRightImage1.png -------------------------------------------------------------------------------- /s3-proxy/s3-proxy/src/test/resources/testRightImage2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woowacourse-teams/2021-pick-git/79b2c28bba8b72b30ef50c5aeecb904dcfba0c93/s3-proxy/s3-proxy/src/test/resources/testRightImage2.png --------------------------------------------------------------------------------