├── .env.development ├── .env.production ├── .github └── workflows │ ├── dev.yml │ └── main.yml ├── .gitignore ├── .prettierrc ├── .vscode └── settings.json ├── README.md ├── asset-license.md ├── config ├── env.js ├── jest │ ├── cssTransform.js │ └── fileTransform.js ├── modules.js ├── paths.js ├── pnpTs.js ├── webpack.config.js ├── webpack.config.server.js ├── webpack.config.serverless.js └── webpackDevServer.config.js ├── deploy-hook ├── .gitignore ├── handler.js ├── package.json ├── serverless.yml └── yarn.lock ├── deploy.config.json ├── docker └── redis │ ├── Dockerfile │ └── docker-compose.yml ├── package.json ├── public ├── favicon.ico ├── favicons │ ├── apple-icon-152x152.png │ ├── favicon-16x16.png │ ├── favicon-32x32.png │ └── favicon-96x96.png ├── index.html ├── logo192.png ├── logo512.png ├── manifest.json └── robots.txt ├── scripts ├── activate ├── activate.dev ├── build.js ├── build.server.js ├── keepChunks.js ├── start.js └── test.js ├── serverless.yml ├── src ├── App.css ├── App.tsx ├── GlobalStyles.ts ├── components │ ├── auth │ │ ├── AuthEmailForm.tsx │ │ ├── AuthEmailSuccess.tsx │ │ ├── AuthForm.tsx │ │ ├── AuthModal.tsx │ │ ├── AuthSocialButton.tsx │ │ ├── AuthSocialButtonGroup.tsx │ │ └── __tests__ │ │ │ ├── AuthEmailSuccess.test.tsx │ │ │ ├── AuthForm.test.tsx │ │ │ └── __snapshots__ │ │ │ └── AuthForm.test.tsx.snap │ ├── base │ │ ├── BodyTransition.tsx │ │ ├── ConditionalBackground.tsx │ │ ├── FloatingHeader.tsx │ │ ├── Header.tsx │ │ ├── HeaderLogo.tsx │ │ ├── HeaderUserIcon.tsx │ │ ├── HeaderUserMenu.tsx │ │ ├── HeaderUserMenuItem.tsx │ │ ├── PageTemplate.tsx │ │ ├── ThemeToggleButton.tsx │ │ ├── __tests__ │ │ │ ├── HeaderLogo.test.tsx │ │ │ ├── HeaderUserIcon.test.tsx │ │ │ └── __snapshots__ │ │ │ │ └── HeaderUserIcon.test.tsx.snap │ │ └── hooks │ │ │ ├── useHeader.ts │ │ │ ├── useThemeEffect.ts │ │ │ └── useToggleTheme.ts │ ├── common │ │ ├── AdFeed.tsx │ │ ├── Button.tsx │ │ ├── DragDropUpload.tsx │ │ ├── EditRemoveGroup.tsx │ │ ├── FlatPostCard.tsx │ │ ├── FlatPostCardList.tsx │ │ ├── FollowButton.tsx │ │ ├── HideScroll.tsx │ │ ├── HorizontalTab.tsx │ │ ├── LabelInput.tsx │ │ ├── MarkdownEditor.tsx │ │ ├── MarkdownRender.tsx │ │ ├── OpaqueLayer.tsx │ │ ├── PaginateWithScroll.tsx │ │ ├── PasteUpload.tsx │ │ ├── PlainLink.tsx │ │ ├── PlainNavLink.tsx │ │ ├── PopupBase.tsx │ │ ├── PopupOKCancel.tsx │ │ ├── PostCard.tsx │ │ ├── PostCardGrid.tsx │ │ ├── PostLink.tsx │ │ ├── PrivatePostLabel.tsx │ │ ├── RatioImage.tsx │ │ ├── RequireLogin.tsx │ │ ├── RoundButton.tsx │ │ ├── ScrollingPagination.tsx │ │ ├── SelectableList.tsx │ │ ├── SetupAdFeed.tsx │ │ ├── Skeleton.tsx │ │ ├── SkeletonTexts.tsx │ │ ├── SorterButton.tsx │ │ ├── SpinnerBlock.tsx │ │ ├── StatusCode.tsx │ │ ├── Sticky.tsx │ │ ├── TagItem.tsx │ │ ├── TagList.tsx │ │ ├── ToggleSwitch.tsx │ │ ├── Typography.tsx │ │ ├── UserProfile.tsx │ │ ├── VLink.tsx │ │ ├── __tests__ │ │ │ ├── PopupOKCancel.test.tsx │ │ │ ├── PostLink.test.tsx │ │ │ ├── RequireLogin.test.tsx │ │ │ ├── SelectableList.test.tsx │ │ │ ├── SorterButton.test.tsx │ │ │ ├── UserProfile.test.tsx │ │ │ └── __snapshots__ │ │ │ │ └── PopupOKCancel.test.tsx.snap │ │ ├── atom-one-dark.css │ │ └── atom-one-light.css │ ├── error │ │ ├── ChunkErrorScreen.tsx │ │ ├── CrashErrorScreen.tsx │ │ ├── ErrorBoundary.tsx │ │ ├── ErrorScreenTemplate.tsx │ │ ├── NetworkErrorScreen.tsx │ │ └── NotFoundError.tsx │ ├── home │ │ ├── HomeLayout.tsx │ │ ├── HomeMobileHeadExtra.tsx │ │ ├── HomeNoticeWidget.tsx │ │ ├── HomeRightFooter.tsx │ │ ├── HomeSidebar.tsx │ │ ├── HomeTab.tsx │ │ ├── HomeTagWidget.tsx │ │ ├── HomeWidget.tsx │ │ ├── TimeframePicker.tsx │ │ ├── hooks │ │ │ └── useTimeframe.ts │ │ └── utils │ │ │ └── timeframeMap.ts │ ├── main │ │ ├── MainPageTemplate.tsx │ │ ├── MainResponsive.tsx │ │ └── MainTemplate.tsx │ ├── policy │ │ ├── PolicyViewer.tsx │ │ └── policyData.ts │ ├── post │ │ ├── JobPositions.tsx │ │ ├── LinkedPostItem.tsx │ │ ├── LinkedPostList.tsx │ │ ├── MobileLikeButton.tsx │ │ ├── PostBanner.tsx │ │ ├── PostCommentItem.tsx │ │ ├── PostCommentsList.tsx │ │ ├── PostCommentsTemplate.tsx │ │ ├── PostCommentsWrite.tsx │ │ ├── PostContent.tsx │ │ ├── PostCustomBanner.tsx │ │ ├── PostHead.tsx │ │ ├── PostHtmlContent.tsx │ │ ├── PostLikeShareButtons.tsx │ │ ├── PostReplies.tsx │ │ ├── PostSeriesInfo.tsx │ │ ├── PostSkeleton.tsx │ │ ├── PostTags.tsx │ │ ├── PostTemplate.tsx │ │ ├── PostToc.tsx │ │ ├── PostViewerProvider.tsx │ │ └── __tests__ │ │ │ ├── LinkedPostItem.test.tsx │ │ │ ├── LinkedPostList.test.tsx │ │ │ ├── MobileLikeButton.test.tsx │ │ │ ├── PostCommentItem.test.tsx │ │ │ ├── PostCommentList.test.tsx │ │ │ ├── PostCommentsTemplate.test.tsx │ │ │ ├── PostCommentsWrite.test.tsx │ │ │ ├── PostContent.test.tsx │ │ │ ├── PostHead.test.tsx │ │ │ ├── PostLikeShareButtons.test.tsx │ │ │ ├── PostReplies.test.tsx │ │ │ ├── PostSeriesInfo.test.tsx │ │ │ └── PostsTags.test.tsx │ ├── postStats │ │ └── PostStats.tsx │ ├── readingList │ │ └── ReadingListTab.tsx │ ├── register │ │ ├── RegisterForm.tsx │ │ ├── RegisterTemplate.tsx │ │ └── __tests__ │ │ │ ├── RegisterForm.test.tsx │ │ │ ├── RegisterTemplate.test.tsx │ │ │ └── __snapshots__ │ │ │ ├── RegisterForm.test.tsx.snap │ │ │ └── RegisterTemplate.test.tsx.snap │ ├── saves │ │ ├── SavedPostItem.tsx │ │ ├── SavedPosts.tsx │ │ ├── SavesTemplate.tsx │ │ └── hooks │ │ │ └── useSavedPosts.ts │ ├── search │ │ ├── SearchInput.tsx │ │ ├── SearchResultInfo.tsx │ │ ├── SearchTemplate.tsx │ │ └── __tests__ │ │ │ └── SearchInput.test.tsx │ ├── setting │ │ ├── SettingEditButton.tsx │ │ ├── SettingEmailRow.tsx │ │ ├── SettingEmailRulesRow.tsx │ │ ├── SettingEmailSuccess.tsx │ │ ├── SettingInput.tsx │ │ ├── SettingRow.tsx │ │ ├── SettingRows.tsx │ │ ├── SettingSocialInfoRow.tsx │ │ ├── SettingTitleRow.tsx │ │ ├── SettingUnregisterRow.tsx │ │ ├── SettingUserProfile.tsx │ │ └── __tests__ │ │ │ └── SettingUserProfile.test.tsx │ ├── tags │ │ ├── DetailedTagItem.tsx │ │ ├── DetailedTagList.tsx │ │ └── TagDetail.tsx │ ├── user-integrate │ │ └── UserIntegrateTemplate.tsx │ ├── velog │ │ ├── DragSample.tsx │ │ ├── DraggableSeriesPostList.tsx │ │ ├── SeriesActionButtons.tsx │ │ ├── SeriesItem.tsx │ │ ├── SeriesList.tsx │ │ ├── SeriesPostItem.tsx │ │ ├── SeriesPostList.tsx │ │ ├── SeriesPostsTemplate.tsx │ │ ├── SeriesSorterAligner.tsx │ │ ├── SideArea.tsx │ │ ├── UserTagHorizontalList.tsx │ │ ├── UserTagVerticalList.tsx │ │ ├── UserTags.tsx │ │ ├── VelogAboutContent.tsx │ │ ├── VelogAboutEdit.tsx │ │ ├── VelogAboutRightButton.tsx │ │ ├── VelogPageTemplate.tsx │ │ ├── VelogResponsive.tsx │ │ ├── VelogSearchInput.tsx │ │ ├── VelogTab.tsx │ │ ├── __tests__ │ │ │ ├── SeriesActionButtons.test.tsx │ │ │ ├── SeriesItem.test.tsx │ │ │ ├── SeriesList.test.tsx │ │ │ ├── SeriesPostItem.test.tsx │ │ │ ├── SeriesPostList.test.tsx │ │ │ ├── SeriesPostsTemplate.test.tsx │ │ │ └── VelogAboutRightButton.test.tsx │ │ └── hooks │ │ │ └── useUserTags.ts │ └── write │ │ ├── AddLink.tsx │ │ ├── AskChangeEditor.tsx │ │ ├── EditorPanes.tsx │ │ ├── MarkdownPreview.tsx │ │ ├── PublishActionButtons.tsx │ │ ├── PublishPreview.tsx │ │ ├── PublishPrivacySetting.tsx │ │ ├── PublishScreenTemplate.tsx │ │ ├── PublishSection.tsx │ │ ├── PublishSeriesConfigButtons.tsx │ │ ├── PublishSeriesConfigTemplate.tsx │ │ ├── PublishSeriesCreate.tsx │ │ ├── PublishSeriesSection.tsx │ │ ├── PublishURLSetting.tsx │ │ ├── QuillEditor.tsx.temp │ │ ├── TagInput.tsx │ │ ├── TitleTextarea.tsx │ │ ├── Toolbar.tsx │ │ ├── WriteFooter.tsx │ │ ├── WriteMarkdownEditor.tsx │ │ └── __tests__ │ │ ├── AskChangeEditor.test.tsx │ │ ├── EditorPanes.test.tsx │ │ ├── MarkdownEditor.test.tsx │ │ ├── MarkdownPreview.test.tsx │ │ ├── PublishActionButtons.test.tsx │ │ ├── PublishPreview.test.tsx │ │ ├── PublishPrivacySetting.test.tsx │ │ ├── PublishScreenTemplate.test.tsx │ │ ├── PublishSection.test.tsx │ │ ├── PublishSeriesConfigButtons.test.tsx │ │ ├── PublishSeriesCreate.test.tsx │ │ ├── PublishSeriesSection.test.tsx │ │ ├── PublishURLSetting.test.tsx │ │ ├── QuillEditor.test.tsx.temp │ │ ├── TagInput.test.tsx │ │ ├── Toolbar.test.tsx │ │ ├── WriteFooter.test.tsx │ │ └── __snapshots__ │ │ ├── AskChangeEditor.test.tsx.snap │ │ ├── MarkdownEditor.test.tsx.snap │ │ ├── MarkdownPreview.test.tsx.snap │ │ ├── PublishActionButtons.test.tsx.snap │ │ ├── PublishPreview.test.tsx.snap │ │ ├── PublishPrivacySetting.test.tsx.snap │ │ ├── PublishScreenTemplate.test.tsx.snap │ │ ├── PublishSection.test.tsx.snap │ │ ├── PublishURLSetting.test.tsx.snap │ │ ├── TagInput.test.tsx.snap │ │ ├── Toolbar.test.tsx.snap │ │ └── WriteFooter.test.tsx.snap ├── containers │ ├── auth │ │ ├── AuthModalContainer.tsx │ │ └── __tests__ │ │ │ ├── AuthModalContainer.test.tsx │ │ │ └── __snapshots__ │ │ │ └── AuthModalContainer.test.tsx.snap │ ├── base │ │ ├── CommonPopup.tsx │ │ ├── Core.tsx │ │ └── hooks │ │ │ ├── useCrisp.tsx │ │ │ └── useUserLoader.tsx │ ├── etc │ │ ├── EmailChange.tsx │ │ └── EmailLogin.tsx │ ├── home │ │ ├── MainNoticeWidgetContainer.tsx │ │ └── MainTagWidgetContainer.tsx │ ├── post │ │ ├── HorizontalAd.tsx │ │ ├── HorizontalBanner.tsx │ │ ├── PostComments.tsx │ │ ├── PostCommentsWriteContainer.tsx │ │ ├── PostEditComment.tsx │ │ ├── PostRepliesContainer.tsx │ │ ├── PostViewer.tsx │ │ ├── RelatedPost.tsx │ │ ├── RelatedPostAd.tsx │ │ ├── RelatedPostsForGuest.tsx │ │ └── __tests__ │ │ │ ├── PostComments.test.tsx │ │ │ ├── PostCommentsWriteContainer.test.tsx │ │ │ └── PostViewer.test.tsx │ ├── register │ │ └── RegisterFormContainer.tsx │ ├── search │ │ ├── LargeSearchInput.tsx │ │ └── SearchResult.tsx │ ├── setting │ │ ├── SettingRowsContainer.tsx │ │ ├── SettingUserProfileContainer.tsx │ │ └── hooks │ │ │ ├── useChangeEmail.ts │ │ │ ├── useUnregister.ts │ │ │ ├── useUpdateEmailRules.ts │ │ │ ├── useUpdateSocialInfo.ts │ │ │ ├── useUpdateThumbnail.ts │ │ │ ├── useUserProfile.ts │ │ │ └── useVelogConfig.ts │ ├── tags │ │ ├── DetailedTagListContainer.tsx │ │ └── TagDetailContainer.tsx │ ├── velog │ │ ├── SeriesListContainer.tsx │ │ ├── SeriesPosts.tsx │ │ ├── UserPosts.tsx │ │ ├── UserProfileContainer.tsx │ │ ├── UserSearchedPosts.tsx │ │ ├── VelogAbout.tsx │ │ ├── VelogPageFallback.tsx │ │ ├── VelogSearchInputContainer.tsx │ │ ├── __tests__ │ │ │ ├── SeriesListContainer.test.tsx │ │ │ ├── SeriesPosts.test.tsx │ │ │ └── UserProfileContainer.test.tsx │ │ └── hooks │ │ │ └── useApplyVelogConfig.ts │ └── write │ │ ├── ActiveEditor.tsx │ │ ├── EditorPanesContainer.tsx │ │ ├── MarkdownEditorContainer.tsx │ │ ├── MarkdownPreviewContainer.tsx │ │ ├── PublishActionButtonsContainer.tsx │ │ ├── PublishCaptcha.tsx │ │ ├── PublishCaptchaContainer.tsx │ │ ├── PublishPreviewContainer.tsx │ │ ├── PublishPrivacySettingContainer.tsx │ │ ├── PublishScreen.tsx │ │ ├── PublishSeriesConfig.tsx │ │ ├── PublishSeriesList.tsx │ │ ├── PublishSeriesSectionContainer.tsx │ │ ├── PublishSettings.tsx │ │ ├── PublishURLSettingContainer.tsx │ │ ├── QuillEditorContainer.tsx.temp │ │ ├── TagInputContainer.tsx │ │ ├── __tests__ │ │ ├── ActiveEditor.test.tsx │ │ ├── MarkdownEditorContainer.test.tsx │ │ ├── PublishPreviewContainer.test.tsx │ │ ├── PublishPrivacySettingContainer.test.tsx │ │ ├── PublishScreen.test.tsx │ │ ├── PublishSeriesSectionContainer.test.tsx │ │ ├── PublishURLSettingContainer.test.tsx │ │ ├── QuillEditorContainer.test.tsx.temp │ │ ├── TagInputContainer.test.tsx │ │ └── __snapshots__ │ │ │ ├── ActiveEditor.test.tsx.snap │ │ │ └── TagInputContainer.test.tsx.snap │ │ └── hooks │ │ └── useSaveHotkey.ts ├── index.css ├── index.server.ts ├── index.tsx ├── lib │ ├── __tests__ │ │ └── utils.test.ts │ ├── api │ │ ├── apiClient.ts │ │ ├── auth.ts │ │ ├── files.ts │ │ └── jobs.ts │ ├── convertToMarkdown.ts.temp │ ├── crisp.ts │ ├── detectIOS.ts │ ├── graphql │ │ ├── UncachedApolloContext.tsx │ │ ├── __data__ │ │ │ ├── post.data.ts │ │ │ ├── series.data.ts │ │ │ └── user.data.ts │ │ ├── ad.ts │ │ ├── client.ts │ │ ├── notification.ts │ │ ├── post.ts │ │ ├── series.ts │ │ ├── tags.ts │ │ ├── types.d.ts │ │ └── user.ts │ ├── gtag.ts │ ├── heading.ts │ ├── hooks │ │ ├── useBoolean.tsx │ │ ├── useCFUpload.ts │ │ ├── useDidMount.ts │ │ ├── useInput.tsx │ │ ├── useInputs.tsx │ │ ├── useMounted.ts │ │ ├── useNotFound.ts │ │ ├── usePopup.tsx │ │ ├── usePrefetchPost.ts │ │ ├── usePreserveScroll.ts │ │ ├── useRequest.tsx │ │ ├── useRequireLogin.ts │ │ ├── useS3Upload.tsx │ │ ├── useScrollPagination.ts │ │ ├── useTheme.ts │ │ ├── useToggle.tsx │ │ ├── useTurnstile.tsx │ │ ├── useUpload.tsx │ │ └── useUser.tsx │ ├── jazzbar │ │ ├── Jazzbar.css │ │ ├── Jazzbar.tsx │ │ ├── JazzbarContext.tsx │ │ ├── index.tsx │ │ └── useJazzbar.tsx │ ├── katexWhitelist.ts │ ├── optimizeImage.ts │ ├── quill │ │ └── markdownShortcuts │ │ │ ├── formats │ │ │ └── hr.js │ │ │ └── index.js │ ├── remark │ │ ├── embedPlugin.ts │ │ └── prismPlugin.js │ ├── renderWithApollo.tsx │ ├── renderWithProviders.tsx │ ├── renderWithRedux.tsx │ ├── replacedModule.ts │ ├── share.ts │ ├── storage.ts │ ├── styles │ │ ├── media.ts │ │ ├── palette.ts │ │ ├── postStyles.ts │ │ ├── prismThemes.ts │ │ ├── responsive.ts │ │ ├── themes.ts │ │ ├── transitions.ts │ │ ├── utils.ts │ │ └── zIndexes.ts │ ├── utils.ts │ └── waitUntil.ts ├── logo copy.svg ├── logo.svg ├── modules │ ├── __tests__ │ │ ├── core.test.ts │ │ ├── header.test.ts │ │ ├── post.test.ts │ │ └── write.test.ts │ ├── core │ │ ├── actions.ts │ │ ├── index.ts │ │ ├── reducer.ts │ │ └── types.ts │ ├── darkMode.ts │ ├── error.ts │ ├── header.ts │ ├── home.ts │ ├── index.ts │ ├── post.ts │ ├── scroll.ts │ └── write.ts ├── pages │ ├── EmailChangePage.tsx │ ├── EmailLoginPage.tsx │ ├── NotFoundPage.tsx │ ├── PolicyPage.tsx │ ├── PostStatsPage.tsx │ ├── RegisterPage.tsx │ ├── SavesPage.tsx │ ├── SearchPage.tsx │ ├── SettingPage.tsx │ ├── SuccessPage.tsx │ ├── UserIntegratePage.tsx │ ├── WritePage.tsx │ ├── home │ │ ├── HomePage.tsx │ │ ├── RecentPostsPage.tsx │ │ ├── TrendingPostsPage.tsx │ │ └── hooks │ │ │ ├── useRecentPosts.ts │ │ │ └── useTrendingPosts.ts │ ├── readingList │ │ ├── ReadingListPage.tsx │ │ └── hooks │ │ │ └── useReadingList.ts │ ├── tags │ │ ├── TagDetailPage.tsx │ │ ├── TagListPage.tsx │ │ └── TagsPage.tsx │ └── velog │ │ ├── PostPage.tsx │ │ ├── SeriesPage.tsx │ │ ├── UserPage.tsx │ │ ├── VelogPage.tsx │ │ └── tabs │ │ ├── AboutTab.tsx │ │ ├── SeriesTab.tsx │ │ └── UserPostsTab.tsx ├── react-app-env.d.ts ├── server │ ├── CacheManager.ts │ ├── Html.tsx │ ├── checkCacheRule.ts │ ├── rateLimitMiddleware.ts │ ├── serverRender.tsx │ └── ssrMiddleware.ts ├── serverless.ts ├── serviceWorker.ts ├── setupTests.ts ├── static │ ├── images │ │ ├── empty-thumbnail.svg │ │ ├── index.ts │ │ ├── pluto-welcome.png │ │ ├── series-thumbnail.svg │ │ ├── undraw_blank_canvas_3rbb.svg │ │ ├── undraw_bug_fixing_oc7a.svg │ │ ├── undraw_empty.svg │ │ ├── undraw_joyride_hnno.svg │ │ ├── undraw_login_v483.svg │ │ ├── undraw_page_not_found_su7k.svg │ │ ├── undraw_searching.svg │ │ ├── undraw_server_down_s4lk.svg │ │ ├── undraw_update_uxn2.svg │ │ └── user-thumbnail.png │ └── svg │ │ ├── icon-add-list.svg │ │ ├── icon-check.svg │ │ ├── icon-clip.svg │ │ ├── icon-email.svg │ │ ├── icon-facebook-square.svg │ │ ├── icon-facebook.svg │ │ ├── icon-github.svg │ │ ├── icon-globe.svg │ │ ├── icon-google.svg │ │ ├── icon-like.svg │ │ ├── icon-lock.svg │ │ ├── icon-minus-box.svg │ │ ├── icon-moon.svg │ │ ├── icon-notification.svg │ │ ├── icon-plus-box.svg │ │ ├── icon-search-2.svg │ │ ├── icon-search-3.svg │ │ ├── icon-search.svg │ │ ├── icon-share-2.svg │ │ ├── icon-share.svg │ │ ├── icon-sun.svg │ │ ├── icon-twitter.svg │ │ ├── image-series.svg │ │ ├── index.ts │ │ ├── logo.svg │ │ ├── vector-image.svg │ │ └── velog-icon.svg ├── types │ ├── missingTypes.d.ts │ └── window.d.ts └── typography.css ├── tsconfig.json └── yarn.lock /.env.development: -------------------------------------------------------------------------------- 1 | PUBLIC_URL=/ 2 | 3 | REACT_APP_CLIENT_V3_HOST=http://localhost:3001 4 | REACT_APP_API_HOST=http://localhost:5002/ 5 | 6 | REACT_APP_GRAPHQL_HOST=http://localhost:5002/ 7 | REACT_APP_GRAPHQL_HOST_NOCDN=https://v2.velog.io/ -------------------------------------------------------------------------------- /.env.production: -------------------------------------------------------------------------------- 1 | PUBLIC_URL=/ 2 | 3 | REACT_APP_CLIENT_V3_HOST=http://localhost:3001 4 | REACT_APP_API_HOST=http://localhost:5002/ 5 | 6 | REACT_APP_GRAPHQL_HOST=https://v2cdn.velog.io/ 7 | REACT_APP_GRAPHQL_HOST_NOCDN=https://v2.velog.io/ -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | 25 | /dist 26 | 27 | # package directories 28 | jspm_packages 29 | 30 | # Serverless directories 31 | .serverless 32 | .webpack 33 | 34 | # ignore setting 35 | .idea 36 | .vscode 37 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "semi": true, 4 | "useTabs": false, 5 | "tabWidth": 2, 6 | "trailingComma": "all", 7 | "printWidth": 80, 8 | "parser": "typescript" 9 | } 10 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "typescript.tsdk": "node_modules/typescript/lib" 3 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## velog-client 2 | 3 | > Velog is a blog platform for developers. It provides compfy markdown editor with syntax highlighter enabled. Currently, this service only supports Korean language. 4 | 5 | Website link: https://velog.io/ 6 | 7 | Backend project of service is at another Repo - [velog-backend](https://github.com/velopert/velog-server) 8 | 9 | ### Project Stack 10 | 11 | - React 12 | - React Router 13 | - TypeScript 14 | - Redux 15 | - Apollo GraphQL 16 | - Styled Components 17 | - Remark 18 | - Codemirror 19 | - Serverless Framework 20 | - AWS Lambda 21 | -------------------------------------------------------------------------------- /asset-license.md: -------------------------------------------------------------------------------- 1 | 1. **[Image Icon](https://www.flaticon.com/free-icon/picture_149092) (WriteScreen)**: 2 | Icons made by [Smashicons](https://www.flaticon.com/authors/smashicons) from [www.flaticon.com](https://www.flaticon.com/) is licensed by [CC 3.0 BY](http://creativecommons.org/licenses/by/3.0/) 3 | 4 |
5 | 6 | 7 | 8 | 9 | Heart Icon: https://iconmonstr.com/favorite-8-svg/ 10 | Clip Icon: https://iconmonstr.com/paperclip-2-svg/ 11 | Check Icon: https://iconmonstr.com/check-mark-1-svg/ -------------------------------------------------------------------------------- /config/jest/cssTransform.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // This is a custom Jest transformer turning style imports into empty objects. 4 | // http://facebook.github.io/jest/docs/en/webpack.html 5 | 6 | module.exports = { 7 | process() { 8 | return 'module.exports = {};'; 9 | }, 10 | getCacheKey() { 11 | // The output is always the same. 12 | return 'cssTransform'; 13 | }, 14 | }; 15 | -------------------------------------------------------------------------------- /config/pnpTs.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const { resolveModuleName } = require('ts-pnp'); 4 | 5 | exports.resolveModuleName = ( 6 | typescript, 7 | moduleName, 8 | containingFile, 9 | compilerOptions, 10 | resolutionHost 11 | ) => { 12 | return resolveModuleName( 13 | moduleName, 14 | containingFile, 15 | compilerOptions, 16 | resolutionHost, 17 | typescript.resolveModuleName 18 | ); 19 | }; 20 | 21 | exports.resolveTypeReferenceDirective = ( 22 | typescript, 23 | moduleName, 24 | containingFile, 25 | compilerOptions, 26 | resolutionHost 27 | ) => { 28 | return resolveModuleName( 29 | moduleName, 30 | containingFile, 31 | compilerOptions, 32 | resolutionHost, 33 | typescript.resolveTypeReferenceDirective 34 | ); 35 | }; 36 | -------------------------------------------------------------------------------- /deploy-hook/.gitignore: -------------------------------------------------------------------------------- 1 | # package directories 2 | node_modules 3 | jspm_packages 4 | 5 | # Serverless directories 6 | .serverless -------------------------------------------------------------------------------- /deploy-hook/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "netlify-completion-hook", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "license": "MIT", 6 | "dependencies": { 7 | "axios": "^0.19.1", 8 | "ioredis": "^4.14.1" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /deploy-hook/serverless.yml: -------------------------------------------------------------------------------- 1 | service: velog-v2-frontend-deploy-hook 2 | 3 | provider: 4 | name: aws 5 | runtime: nodejs12.x 6 | region: ap-northeast-2 7 | stage: prod 8 | 9 | environment: 10 | GITHUB_TOKEN: ${ssm:/velog-v2/github-token} 11 | SSR_DEPLOY_TOKEN: ${ssm:/velog-v2/ssr-deploy-token} 12 | REDIS_HOST: ${ssm:/velog-v2/redis-host} 13 | 14 | vpc: 15 | securityGroupIds: 16 | - sg-007a5a395dbdcef1f 17 | - sg-f588c99d 18 | - sg-0fd14591289bb3212 19 | subnetIds: 20 | - subnet-02c6112f71f5148c7 21 | - subnet-0ebc43e6ab298c646 22 | 23 | functions: 24 | webhook: 25 | handler: handler.webhook 26 | events: 27 | - http: 28 | path: / 29 | method: POST 30 | 31 | -------------------------------------------------------------------------------- /deploy.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "apps": [ 3 | { 4 | "name": "ssr", 5 | "script": "./dist/server.js", 6 | "instances": 2, 7 | "exec_mode": "cluster", 8 | "env": { 9 | "NODE_PATH": "src" 10 | } 11 | } 12 | ] 13 | } -------------------------------------------------------------------------------- /docker/redis/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM bitnami/redis:5.0 2 | ENV ALLOW_EMPTY_PASSWORD=yes 3 | -------------------------------------------------------------------------------- /docker/redis/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '2' 2 | 3 | services: 4 | redis: 5 | image: 'bitnami/redis:5.0' 6 | environment: 7 | # ALLOW_EMPTY_PASSWORD is recommended only for development. 8 | - ALLOW_EMPTY_PASSWORD=yes 9 | ports: 10 | - '6379:6379' 11 | volumes: 12 | - 'redis_data:/bitnami/redis/data' 13 | 14 | volumes: 15 | redis_data: 16 | driver: local 17 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/velopert/velog-client/65e23035d9e65efac1c266d6744fd7bc16fda632/public/favicon.ico -------------------------------------------------------------------------------- /public/favicons/apple-icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/velopert/velog-client/65e23035d9e65efac1c266d6744fd7bc16fda632/public/favicons/apple-icon-152x152.png -------------------------------------------------------------------------------- /public/favicons/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/velopert/velog-client/65e23035d9e65efac1c266d6744fd7bc16fda632/public/favicons/favicon-16x16.png -------------------------------------------------------------------------------- /public/favicons/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/velopert/velog-client/65e23035d9e65efac1c266d6744fd7bc16fda632/public/favicons/favicon-32x32.png -------------------------------------------------------------------------------- /public/favicons/favicon-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/velopert/velog-client/65e23035d9e65efac1c266d6744fd7bc16fda632/public/favicons/favicon-96x96.png -------------------------------------------------------------------------------- /public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/velopert/velog-client/65e23035d9e65efac1c266d6744fd7bc16fda632/public/logo192.png -------------------------------------------------------------------------------- /public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/velopert/velog-client/65e23035d9e65efac1c266d6744fd7bc16fda632/public/logo512.png -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | -------------------------------------------------------------------------------- /scripts/activate: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | export PUBLIC_URL=https://static.velog.io/ 3 | export REACT_APP_API_HOST=https://v2.velog.io/ 4 | export S3_BUCKET=static.velog.io -------------------------------------------------------------------------------- /scripts/activate.dev: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | export PUBLIC_URL=https://d3v0gm8v6v8olv.cloudfront.net/ 3 | export REACT_APP_API_HOST=https://v2dev.velog.io/ 4 | export STAGE=true 5 | export S3_BUCKET=dev.static.velog.io -------------------------------------------------------------------------------- /scripts/build.server.js: -------------------------------------------------------------------------------- 1 | // Do this as the first thing so that any code reading it knows the right env. 2 | process.env.BABEL_ENV = 'production'; 3 | process.env.NODE_ENV = 'production'; 4 | process.env.REACT_APP_SSR = 'enabled'; 5 | 6 | // Makes the script crash on unhandled rejections instead of silently 7 | // ignoring them. In the future, promise rejections that are not handled will 8 | // terminate the Node.js process with a non-zero exit code. 9 | process.on('unhandledRejection', err => { 10 | throw err; 11 | }); 12 | 13 | // Ensure environment variables are read. 14 | require('../config/env'); 15 | const fs = require('fs-extra'); 16 | const webpack = require('webpack'); 17 | const config = require('../config/webpack.config.server'); 18 | const paths = require('../config/paths'); 19 | 20 | function build() { 21 | const compiler = webpack({ ...config }); 22 | fs.emptyDirSync(paths.ssrBuild); 23 | return new Promise((resolve, reject) => { 24 | compiler.run((err, stats) => { 25 | if (err) { 26 | console.log(err); 27 | return; 28 | } 29 | }); 30 | }); 31 | } 32 | 33 | build(); 34 | -------------------------------------------------------------------------------- /src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | height: 40vmin; 7 | pointer-events: none; 8 | } 9 | 10 | @media (prefers-reduced-motion: no-preference) { 11 | .App-logo { 12 | animation: App-logo-spin infinite 20s linear; 13 | } 14 | } 15 | 16 | .App-header { 17 | background-color: #282c34; 18 | min-height: 100vh; 19 | display: flex; 20 | flex-direction: column; 21 | align-items: center; 22 | justify-content: center; 23 | font-size: calc(10px + 2vmin); 24 | color: white; 25 | } 26 | 27 | .App-link { 28 | color: #61dafb; 29 | } 30 | 31 | @keyframes App-logo-spin { 32 | from { 33 | transform: rotate(0deg); 34 | } 35 | to { 36 | transform: rotate(360deg); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/GlobalStyles.ts: -------------------------------------------------------------------------------- 1 | import { createGlobalStyle } from 'styled-components'; 2 | import { themedPalette, themes } from './lib/styles/themes'; 3 | 4 | const GlobalStyles = createGlobalStyle` 5 | body { 6 | margin: 0; 7 | padding: 0; 8 | font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", "Apple SD Gothic Neo", "Malgun Gothic", "맑은 고딕", 나눔고딕, "Nanum Gothic", "Noto Sans KR", "Noto Sans CJK KR", arial, 돋움, Dotum, Tahoma, Geneva, sans-serif; 9 | -webkit-font-smoothing: antialiased; 10 | -moz-osx-font-smoothing: grayscale; 11 | color: ${themedPalette.text1}; 12 | box-sizing: border-box; 13 | 14 | } 15 | 16 | * { 17 | box-sizing: inherit; 18 | } 19 | 20 | code { 21 | font-family: 'Fira Mono', source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 22 | monospace; 23 | } 24 | 25 | input, button, textarea { 26 | font-family: inherit; 27 | } 28 | 29 | html, body, #root { 30 | height: 100%; 31 | } 32 | 33 | body { 34 | ${themes.light} 35 | } 36 | 37 | @media (prefers-color-scheme: dark) { 38 | body { 39 | ${themes.dark} 40 | } 41 | } 42 | 43 | body[data-theme='light'] { 44 | ${themes.light}; 45 | } 46 | 47 | body[data-theme='dark'] { 48 | ${themes.dark}; 49 | } 50 | 51 | `; 52 | 53 | export default GlobalStyles; 54 | -------------------------------------------------------------------------------- /src/components/auth/AuthSocialButtonGroup.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import styled from 'styled-components'; 3 | import AuthSocialButton from './AuthSocialButton'; 4 | 5 | const AuthSocialButtonGroupBlock = styled.div` 6 | display: flex; 7 | justify-content: space-around; 8 | margin-top: 1.5rem; 9 | `; 10 | 11 | const AuthSocialButtonGroup = ({ 12 | currentPath, 13 | isIntegrate, 14 | integrateState, 15 | }: { 16 | currentPath: string; 17 | isIntegrate?: boolean; 18 | integrateState?: string; 19 | }) => { 20 | return ( 21 |