├── .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 |
4 |
5 |
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 |
8 |
--------------------------------------------------------------------------------
/frontend/src/assets/icons/add-circle-large.svg:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------
/frontend/src/assets/icons/add-circle.svg:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------
/frontend/src/assets/icons/arrow-down.svg:
--------------------------------------------------------------------------------
1 |
6 |
--------------------------------------------------------------------------------
/frontend/src/assets/icons/arrow-right.svg:
--------------------------------------------------------------------------------
1 |
6 |
--------------------------------------------------------------------------------
/frontend/src/assets/icons/blog.svg:
--------------------------------------------------------------------------------
1 |
7 |
--------------------------------------------------------------------------------
/frontend/src/assets/icons/book.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/frontend/src/assets/icons/briefcase-solid.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/frontend/src/assets/icons/camera.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/frontend/src/assets/icons/cancel-no-circle.svg:
--------------------------------------------------------------------------------
1 |
7 |
--------------------------------------------------------------------------------
/frontend/src/assets/icons/clock.svg:
--------------------------------------------------------------------------------
1 |
7 |
--------------------------------------------------------------------------------
/frontend/src/assets/icons/company-line.svg:
--------------------------------------------------------------------------------
1 |
7 |
--------------------------------------------------------------------------------
/frontend/src/assets/icons/copy.svg:
--------------------------------------------------------------------------------
1 |
7 |
--------------------------------------------------------------------------------
/frontend/src/assets/icons/delete-circle.svg:
--------------------------------------------------------------------------------
1 |
7 |
--------------------------------------------------------------------------------
/frontend/src/assets/icons/delete.svg:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------
/frontend/src/assets/icons/edit.svg:
--------------------------------------------------------------------------------
1 |
6 |
--------------------------------------------------------------------------------
/frontend/src/assets/icons/email.svg:
--------------------------------------------------------------------------------
1 |
7 |
--------------------------------------------------------------------------------
/frontend/src/assets/icons/github-large.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/frontend/src/assets/icons/github-line.svg:
--------------------------------------------------------------------------------
1 |
6 |
--------------------------------------------------------------------------------
/frontend/src/assets/icons/github-repository.svg:
--------------------------------------------------------------------------------
1 |
6 |
--------------------------------------------------------------------------------
/frontend/src/assets/icons/go-back.svg:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------
/frontend/src/assets/icons/go-down.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/frontend/src/assets/icons/go-forward.svg:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------
/frontend/src/assets/icons/heart-line.svg:
--------------------------------------------------------------------------------
1 |
6 |
--------------------------------------------------------------------------------
/frontend/src/assets/icons/heart.svg:
--------------------------------------------------------------------------------
1 |
6 |
--------------------------------------------------------------------------------
/frontend/src/assets/icons/home.svg:
--------------------------------------------------------------------------------
1 |
7 |
--------------------------------------------------------------------------------
/frontend/src/assets/icons/issue.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/frontend/src/assets/icons/link.svg:
--------------------------------------------------------------------------------
1 |
7 |
--------------------------------------------------------------------------------
/frontend/src/assets/icons/location-line.svg:
--------------------------------------------------------------------------------
1 |
7 |
--------------------------------------------------------------------------------
/frontend/src/assets/icons/login.svg:
--------------------------------------------------------------------------------
1 |
7 |
--------------------------------------------------------------------------------
/frontend/src/assets/icons/map-marker-alt-solid.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/frontend/src/assets/icons/person.svg:
--------------------------------------------------------------------------------
1 |
7 |
--------------------------------------------------------------------------------
/frontend/src/assets/icons/phone.svg:
--------------------------------------------------------------------------------
1 |
6 |
--------------------------------------------------------------------------------
/frontend/src/assets/icons/post-heart-line.svg:
--------------------------------------------------------------------------------
1 |
6 |
--------------------------------------------------------------------------------
/frontend/src/assets/icons/post-heart.svg:
--------------------------------------------------------------------------------
1 |
6 |
--------------------------------------------------------------------------------
/frontend/src/assets/icons/search.svg:
--------------------------------------------------------------------------------
1 |
7 |
--------------------------------------------------------------------------------
/frontend/src/assets/icons/send.svg:
--------------------------------------------------------------------------------
1 |
7 |
--------------------------------------------------------------------------------
/frontend/src/assets/icons/star.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/frontend/src/assets/icons/vertical-dots.svg:
--------------------------------------------------------------------------------
1 |
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 |
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
--------------------------------------------------------------------------------