├── .dockerignore ├── .github └── workflows │ ├── copilot-instructions.md │ └── docker.yml ├── .gitignore ├── .vscode └── settings.json ├── Dockerfile ├── LICENSE ├── README.md ├── admin ├── .gitignore ├── README.md ├── babel.config.js ├── jsconfig.json ├── package.json ├── public │ ├── awesome │ │ ├── css │ │ │ └── all.min.css │ │ └── webfonts │ │ │ ├── fa-brands-400.eot │ │ │ ├── fa-brands-400.svg │ │ │ ├── fa-brands-400.ttf │ │ │ ├── fa-brands-400.woff │ │ │ ├── fa-brands-400.woff2 │ │ │ ├── fa-regular-400.eot │ │ │ ├── fa-regular-400.svg │ │ │ ├── fa-regular-400.ttf │ │ │ ├── fa-regular-400.woff │ │ │ ├── fa-regular-400.woff2 │ │ │ ├── fa-solid-900.eot │ │ │ ├── fa-solid-900.svg │ │ │ ├── fa-solid-900.ttf │ │ │ ├── fa-solid-900.woff │ │ │ └── fa-solid-900.woff2 │ ├── favicon.png │ └── index.html ├── src │ ├── App.vue │ ├── api │ │ ├── create-api.js │ │ ├── index.js │ │ └── module │ │ │ └── auth.js │ ├── assets │ │ ├── css │ │ │ └── common.css │ │ └── logo.png │ ├── components │ │ ├── AlbumEditorDialog.vue │ │ ├── AlbumTable.vue │ │ ├── AttachmentImage.vue │ │ ├── AttachmentsDialog.vue │ │ ├── BackupEditor.vue │ │ ├── BangumiSelector.vue │ │ ├── BookSelector.vue │ │ ├── BooktypeEditor.vue │ │ ├── CheckDialog.vue │ │ ├── CheckOption.vue │ │ ├── ConfigAdSettingsForm.vue │ │ ├── ConfigCommentSettingsForm.vue │ │ ├── ConfigEmailSettingsForm.vue │ │ ├── ConfigMediaForm.vue │ │ ├── ConfigOtherSettingsForm.vue │ │ ├── ConfigRssSettingsForm.vue │ │ ├── ConfigSitePostForm.vue │ │ ├── ConfigSiteSettingsForm.vue │ │ ├── Cropper.vue │ │ ├── Emoji.vue │ │ ├── EmojiTextarea.vue │ │ ├── EventSelector.vue │ │ ├── EventtypeEditor.vue │ │ ├── GamePlatformEditor.vue │ │ ├── GameSelector.vue │ │ ├── GoogleAdInput.vue │ │ ├── IpInfoDisplay.vue │ │ ├── MovieSelector.vue │ │ ├── PostBatchForm.vue │ │ ├── PostSelector.vue │ │ ├── RichEditor4.vue │ │ ├── RichEditor5.vue │ │ ├── RichEditor5Switch.vue │ │ ├── RichEditorEventSelectorDialog.vue │ │ ├── SortSelector.vue │ │ ├── Statistics.vue │ │ ├── StatisticsReader.vue │ │ ├── StringSortEditBox.vue │ │ ├── TagSelector.vue │ │ ├── ThemeChanger.vue │ │ ├── UserDeleteDialog.vue │ │ ├── VideoUploader.vue │ │ └── VoteSelector.vue │ ├── composables │ │ └── useTheme.js │ ├── main.js │ ├── router │ │ └── index.js │ ├── services │ │ └── CheckDialogService.js │ ├── store │ │ └── index.js │ ├── utils │ │ ├── editorMenu │ │ │ ├── event │ │ │ │ ├── elem-to-html.js │ │ │ │ ├── index.js │ │ │ │ ├── menu │ │ │ │ │ ├── editEventspan.js │ │ │ │ │ ├── menu.js │ │ │ │ │ └── unsetEventspan.js │ │ │ │ ├── parse-elem-html.js │ │ │ │ ├── plugin.js │ │ │ │ └── render-elem.js │ │ │ ├── image │ │ │ │ ├── index.js │ │ │ │ └── menu │ │ │ │ │ ├── menu33.js │ │ │ │ │ └── menuauto.js │ │ │ ├── imageGroup │ │ │ │ ├── elem-to-html.js │ │ │ │ ├── index.js │ │ │ │ ├── menu │ │ │ │ │ └── menu.js │ │ │ │ ├── parse-elem-html.js │ │ │ │ ├── plugin.js │ │ │ │ └── render-elem.js │ │ │ ├── panorama360 │ │ │ │ ├── elem-to-html.js │ │ │ │ ├── index.js │ │ │ │ ├── menu │ │ │ │ │ └── menu.js │ │ │ │ ├── parse-elem-html.js │ │ │ │ ├── plugin.js │ │ │ │ └── render-elem.js │ │ │ └── utils.js │ │ ├── emoji.json │ │ ├── richEditor.js │ │ ├── theme.js │ │ └── utils.js │ └── views │ │ ├── Login.vue │ │ └── index │ │ ├── Index.vue │ │ ├── album │ │ └── AlbumList.vue │ │ ├── backup │ │ └── BackupList.vue │ │ ├── bangumi │ │ ├── BangumiEditor.vue │ │ └── BangumiList.vue │ │ ├── banner │ │ └── BannerList.vue │ │ ├── book │ │ ├── BookEditor.vue │ │ └── BookList.vue │ │ ├── booktype │ │ └── BooktypeList.vue │ │ ├── comment │ │ ├── CommentEditor.vue │ │ └── CommentList.vue │ │ ├── commentLikeLog │ │ └── CommentLikeLogList.vue │ │ ├── config │ │ └── Config.vue │ │ ├── emailSendHistory │ │ └── EmailSendHistoryList.vue │ │ ├── event │ │ ├── EventEditor.vue │ │ └── EventList.vue │ │ ├── eventtype │ │ └── EventtypeList.vue │ │ ├── game │ │ ├── GameEditor.vue │ │ └── GameList.vue │ │ ├── gamePlatform │ │ └── GamePlatformList.vue │ │ ├── home │ │ └── Home.vue │ │ ├── link │ │ ├── LinkEditor.vue │ │ └── LinkList.vue │ │ ├── loginuser │ │ └── LoginUserEditor.vue │ │ ├── movie │ │ ├── MovieEditor.vue │ │ └── MovieList.vue │ │ ├── navi │ │ ├── NaviEditor.vue │ │ └── NaviList.vue │ │ ├── post │ │ ├── PostEditor.vue │ │ └── PostList.vue │ │ ├── postLikeLog │ │ └── PostLikeLogList.vue │ │ ├── readerlog │ │ └── ReaderlogList.vue │ │ ├── referrer │ │ └── ReferrerList.vue │ │ ├── rsslog │ │ └── RsslogList.vue │ │ ├── sidebar │ │ └── SidebarList.vue │ │ ├── sort │ │ ├── SortEditor.vue │ │ └── SortList.vue │ │ ├── tag │ │ ├── TagEditor.vue │ │ └── TagList.vue │ │ ├── user │ │ ├── UserAdd.vue │ │ ├── UserEditor.vue │ │ └── UserList.vue │ │ ├── vote │ │ ├── VoteEditor.vue │ │ └── VoteList.vue │ │ └── votelog │ │ └── VotelogList.vue ├── tools │ └── createCRUD.js ├── vue.config.js └── yarn.lock ├── blog ├── .gitignore ├── Dockerfile ├── README.md ├── api │ ├── attachment.ts │ ├── bangumi.ts │ ├── banner.ts │ ├── book.ts │ ├── comment.ts │ ├── event.ts │ ├── game.ts │ ├── index.ts │ ├── link.ts │ ├── log.ts │ ├── movie.ts │ ├── navi.ts │ ├── option.ts │ ├── post.ts │ ├── sidebar.ts │ ├── sort.ts │ ├── tag.ts │ ├── trend.ts │ └── vote.ts ├── app.config.ts ├── app.vue ├── assets │ └── css │ │ └── common.css ├── build │ └── package.json ├── components │ ├── Adsbygoogle.vue │ ├── AdsbygoogleHave.vue │ ├── AlbumPhotoSwipe.vue │ ├── Archive.vue │ ├── Avatar.vue │ ├── BangumiItem.vue │ ├── BangumiSeasonList.vue │ ├── Banner.vue │ ├── BookItem.vue │ ├── Calendar.vue │ ├── CommentForm.vue │ ├── CommentLatest.vue │ ├── CommentRetractBtn.vue │ ├── CommonDialog.vue │ ├── DivLoading.vue │ ├── Emoji.vue │ ├── Empty.vue │ ├── EventDialog.vue │ ├── GameItem.vue │ ├── GoTop.vue │ ├── HtmlContent.vue │ ├── LoadingDots.vue │ ├── MovieItem.vue │ ├── NaviItem.vue │ ├── PageAbout.vue │ ├── PageAlmanac.vue │ ├── PageBangumi.vue │ ├── PageBookList.vue │ ├── PageEvent.vue │ ├── PageGameList.vue │ ├── PageLink.vue │ ├── PageMovieList.vue │ ├── PageSeeking.vue │ ├── PhotoSwipe.vue │ ├── PlayingGameList.vue │ ├── PostACG.vue │ ├── PostAbout.vue │ ├── PostAboutEvent.vue │ ├── PostCommonFooter.vue │ ├── PostDetail.vue │ ├── PostHeaderItem.vue │ ├── PostItem.vue │ ├── PostList.vue │ ├── PostListFilterBtn.vue │ ├── PostPageRandomPostList.vue │ ├── PostShowHeaderListBtn.vue │ ├── PostVote.vue │ ├── RandomTagList.vue │ ├── Rating.vue │ ├── ReadingBookList.vue │ ├── Sort.vue │ ├── ThemeChanger.vue │ ├── TrendPostList.vue │ ├── TweetContent.vue │ ├── TweetImgList.vue │ ├── VoteItem.vue │ └── WikimoeImage.vue ├── error.vue ├── layouts │ └── default.vue ├── nuxt.config.ts ├── package.json ├── pages │ ├── 404.vue │ ├── index.vue │ └── index │ │ ├── page │ │ └── [id].vue │ │ └── post │ │ ├── [id].vue │ │ ├── list.vue │ │ └── list │ │ ├── [[page]] │ │ └── [[type]].vue │ │ ├── archive │ │ └── [year] │ │ │ ├── [month].vue │ │ │ └── [month] │ │ │ └── [page] │ │ │ └── [[type]].vue │ │ ├── keyword │ │ ├── [keyword].vue │ │ └── [keyword] │ │ │ └── [page] │ │ │ └── [[type]].vue │ │ ├── sort │ │ ├── [sortid].vue │ │ └── [sortid] │ │ │ └── [page] │ │ │ └── [[type]].vue │ │ └── tag │ │ ├── [tagid].vue │ │ └── [tagid] │ │ └── [page] │ │ └── [[type]].vue ├── public │ ├── favicon.ico │ └── img │ │ ├── avatar │ │ ├── 0.webp │ │ ├── 1.webp │ │ ├── 10.webp │ │ ├── 100.webp │ │ ├── 101.webp │ │ ├── 102.webp │ │ ├── 103.webp │ │ ├── 104.webp │ │ ├── 105.webp │ │ ├── 106.webp │ │ ├── 107.webp │ │ ├── 108.webp │ │ ├── 109.webp │ │ ├── 11.webp │ │ ├── 110.webp │ │ ├── 111.webp │ │ ├── 112.webp │ │ ├── 113.webp │ │ ├── 114.webp │ │ ├── 115.webp │ │ ├── 116.webp │ │ ├── 117.webp │ │ ├── 118.webp │ │ ├── 119.webp │ │ ├── 12.webp │ │ ├── 120.webp │ │ ├── 121.webp │ │ ├── 122.webp │ │ ├── 123.webp │ │ ├── 124.webp │ │ ├── 125.webp │ │ ├── 126.webp │ │ ├── 127.webp │ │ ├── 128.webp │ │ ├── 129.webp │ │ ├── 13.webp │ │ ├── 130.webp │ │ ├── 131.webp │ │ ├── 132.webp │ │ ├── 133.webp │ │ ├── 134.webp │ │ ├── 135.webp │ │ ├── 136.webp │ │ ├── 137.webp │ │ ├── 138.webp │ │ ├── 139.webp │ │ ├── 14.webp │ │ ├── 140.webp │ │ ├── 141.webp │ │ ├── 142.webp │ │ ├── 143.webp │ │ ├── 144.webp │ │ ├── 145.webp │ │ ├── 146.webp │ │ ├── 147.webp │ │ ├── 148.webp │ │ ├── 149.webp │ │ ├── 15.webp │ │ ├── 150.webp │ │ ├── 151.webp │ │ ├── 152.webp │ │ ├── 153.webp │ │ ├── 154.webp │ │ ├── 155.webp │ │ ├── 156.webp │ │ ├── 157.webp │ │ ├── 158.webp │ │ ├── 159.webp │ │ ├── 16.webp │ │ ├── 160.webp │ │ ├── 161.webp │ │ ├── 162.webp │ │ ├── 163.webp │ │ ├── 164.webp │ │ ├── 165.webp │ │ ├── 166.webp │ │ ├── 167.webp │ │ ├── 168.webp │ │ ├── 169.webp │ │ ├── 17.webp │ │ ├── 170.webp │ │ ├── 171.webp │ │ ├── 172.webp │ │ ├── 173.webp │ │ ├── 174.webp │ │ ├── 175.webp │ │ ├── 176.webp │ │ ├── 18.webp │ │ ├── 19.webp │ │ ├── 2.webp │ │ ├── 20.webp │ │ ├── 21.webp │ │ ├── 22.webp │ │ ├── 23.webp │ │ ├── 24.webp │ │ ├── 25.webp │ │ ├── 26.webp │ │ ├── 27.webp │ │ ├── 28.webp │ │ ├── 29.webp │ │ ├── 3.webp │ │ ├── 30.webp │ │ ├── 31.webp │ │ ├── 32.webp │ │ ├── 33.webp │ │ ├── 34.webp │ │ ├── 35.webp │ │ ├── 36.webp │ │ ├── 37.webp │ │ ├── 38.webp │ │ ├── 39.webp │ │ ├── 4.webp │ │ ├── 40.webp │ │ ├── 41.webp │ │ ├── 42.webp │ │ ├── 43.webp │ │ ├── 44.webp │ │ ├── 45.webp │ │ ├── 46.webp │ │ ├── 47.webp │ │ ├── 48.webp │ │ ├── 49.webp │ │ ├── 5.webp │ │ ├── 50.webp │ │ ├── 51.webp │ │ ├── 52.webp │ │ ├── 53.webp │ │ ├── 54.webp │ │ ├── 55.webp │ │ ├── 56.webp │ │ ├── 57.webp │ │ ├── 58.webp │ │ ├── 59.webp │ │ ├── 6.webp │ │ ├── 60.webp │ │ ├── 61.webp │ │ ├── 62.webp │ │ ├── 63.webp │ │ ├── 64.webp │ │ ├── 65.webp │ │ ├── 66.webp │ │ ├── 67.webp │ │ ├── 68.webp │ │ ├── 69.webp │ │ ├── 7.webp │ │ ├── 70.webp │ │ ├── 71.webp │ │ ├── 72.webp │ │ ├── 73.webp │ │ ├── 74.webp │ │ ├── 75.webp │ │ ├── 76.webp │ │ ├── 77.webp │ │ ├── 78.webp │ │ ├── 79.webp │ │ ├── 8.webp │ │ ├── 80.webp │ │ ├── 81.webp │ │ ├── 82.webp │ │ ├── 83.webp │ │ ├── 84.webp │ │ ├── 85.webp │ │ ├── 86.webp │ │ ├── 87.webp │ │ ├── 88.webp │ │ ├── 89.webp │ │ ├── 9.webp │ │ ├── 90.webp │ │ ├── 91.webp │ │ ├── 92.webp │ │ ├── 93.webp │ │ ├── 94.webp │ │ ├── 95.webp │ │ ├── 96.webp │ │ ├── 97.webp │ │ ├── 98.webp │ │ └── 99.webp │ │ ├── bg_02.png │ │ ├── bg_02_dark.png │ │ ├── menuBg.png │ │ ├── mypage-banner.webp │ │ ├── nodata.webp │ │ └── nopic400-565.png ├── sample.env ├── server │ └── tsconfig.json ├── store │ ├── commentRetractAuthDecode.ts │ ├── commentRetractCountData.ts │ ├── isFullscreen.ts │ └── options.ts ├── tailwind.config.ts ├── tsconfig.json ├── utils │ ├── emoji.json │ └── index.ts └── yarn.lock ├── build-all.js ├── docker-compose-lite.yml ├── docker-compose.yml ├── example.env ├── index.js ├── package.json ├── server ├── api │ ├── admin │ │ ├── album │ │ │ ├── createAlbum.js │ │ │ ├── deleteAlbum.js │ │ │ ├── getAlbumDetail.js │ │ │ ├── getAlbumList.js │ │ │ └── updateAlbum.js │ │ ├── attachment │ │ │ ├── deleteAttachment.js │ │ │ ├── getAttachmentList.js │ │ │ ├── updateAttachmentAlbum.js │ │ │ ├── updateAttachmentInfo.js │ │ │ ├── uploadAttachment.js │ │ │ └── uploadAttachmentVideo.js │ │ ├── backup │ │ │ ├── createBackup.js │ │ │ ├── createBackupUpload.js │ │ │ ├── deleteBackup.js │ │ │ ├── downloadBackup.js │ │ │ ├── getBackupDetail.js │ │ │ ├── getBackupList.js │ │ │ ├── getDownloadBackupToken.js │ │ │ ├── getUploadBackupFileChunkList.js │ │ │ ├── mergeUploadBackupFile.js │ │ │ ├── restoreBackup.js │ │ │ ├── updateBackup.js │ │ │ └── uploadBackupFileChunk.js │ │ ├── bangumi │ │ │ ├── createBangumi.js │ │ │ ├── deleteBangumi.js │ │ │ ├── getBangumiDetail.js │ │ │ ├── getBangumiList.js │ │ │ └── updateBangumi.js │ │ ├── banner │ │ │ ├── createBanner.js │ │ │ ├── deleteBanner.js │ │ │ ├── getBannerList.js │ │ │ ├── updateBanner.js │ │ │ ├── updateBannerImg.js │ │ │ └── updateBannerTaxis.js │ │ ├── book │ │ │ ├── createBook.js │ │ │ ├── deleteBook.js │ │ │ ├── getBookDetail.js │ │ │ ├── getBookList.js │ │ │ └── updateBook.js │ │ ├── booktype │ │ │ ├── createBooktype.js │ │ │ ├── deleteBooktype.js │ │ │ ├── getBooktypeDetail.js │ │ │ ├── getBooktypeList.js │ │ │ └── updateBooktype.js │ │ ├── comment │ │ │ ├── applyComment.js │ │ │ ├── createComment.js │ │ │ ├── deleteComment.js │ │ │ ├── getCommentDetail.js │ │ │ ├── getCommentList.js │ │ │ └── updateComment.js │ │ ├── commentLikeLog │ │ │ ├── deleteCommentLikeLog.js │ │ │ ├── getCommentLikeLogList.js │ │ │ └── getCommentLikeLogStats.js │ │ ├── config │ │ │ ├── getConfig.js │ │ │ └── updateConfigMedia.js │ │ ├── dashboard │ │ │ ├── getDashboard.js │ │ │ └── getVisitor.js │ │ ├── emailSendHistory │ │ │ ├── getEmailSendHistoryList.js │ │ │ └── resendEmail.js │ │ ├── event │ │ │ ├── createEvent.js │ │ │ ├── deleteEvent.js │ │ │ ├── getEventDetail.js │ │ │ ├── getEventList.js │ │ │ └── updateEvent.js │ │ ├── eventtype │ │ │ ├── createEventtype.js │ │ │ ├── deleteEventtype.js │ │ │ ├── getEventtypeDetail.js │ │ │ ├── getEventtypeList.js │ │ │ └── updateEventtype.js │ │ ├── game │ │ │ ├── createGame.js │ │ │ ├── deleteGame.js │ │ │ ├── getGameDetail.js │ │ │ ├── getGameList.js │ │ │ └── updateGame.js │ │ ├── gamePlatform │ │ │ ├── createGamePlatform.js │ │ │ ├── deleteGamePlatform.js │ │ │ ├── getGamePlatformDetail.js │ │ │ ├── getGamePlatformList.js │ │ │ └── updateGamePlatform.js │ │ ├── link │ │ │ ├── createlink.js │ │ │ ├── deletelink.js │ │ │ ├── getlinkDetail.js │ │ │ ├── getlinkList.js │ │ │ └── updatelink.js │ │ ├── login.js │ │ ├── movie │ │ │ ├── createMovie.js │ │ │ ├── deleteMovie.js │ │ │ ├── getMovieDetail.js │ │ │ ├── getMovieList.js │ │ │ └── updateMovie.js │ │ ├── navi │ │ │ ├── createnavi.js │ │ │ ├── deletenavi.js │ │ │ ├── getnaviDetail.js │ │ │ ├── getnaviList.js │ │ │ └── updatenavi.js │ │ ├── option │ │ │ ├── getoptionList.js │ │ │ ├── reflushJWTScretAdmin.js │ │ │ ├── reflushJWTScretBlog.js │ │ │ └── updateoption.js │ │ ├── post │ │ │ ├── batch.js │ │ │ ├── createPost.js │ │ │ ├── deletePost.js │ │ │ ├── getPostDetail.js │ │ │ ├── getPostList.js │ │ │ ├── updatePost.js │ │ │ └── updatePostEditorVersion.js │ │ ├── postLikeLog │ │ │ ├── deletePostLikeLog.js │ │ │ ├── getPostLikeLogList.js │ │ │ └── getPostLikeLogStats.js │ │ ├── readerlog │ │ │ ├── deleteReaderlog.js │ │ │ ├── getReaderlogList.js │ │ │ └── getReaderlogStats.js │ │ ├── referrer │ │ │ └── getReferrerList.js │ │ ├── rsslog │ │ │ └── getRsslogList.js │ │ ├── sidebar │ │ │ ├── createSidebar.js │ │ │ ├── deleteSidebar.js │ │ │ ├── getSidebarDetail.js │ │ │ ├── getSidebarList.js │ │ │ ├── updateSidebar.js │ │ │ └── updateSidebarTaxis.js │ │ ├── sort │ │ │ ├── createSort.js │ │ │ ├── deleteSort.js │ │ │ ├── getSortDetail.js │ │ │ ├── getSortList.js │ │ │ └── updateSort.js │ │ ├── statistics │ │ │ └── getStatistics.js │ │ ├── tag │ │ │ ├── createTag.js │ │ │ ├── deleteTag.js │ │ │ ├── getTagDetail.js │ │ │ ├── getTagList.js │ │ │ ├── updateTag.js │ │ │ └── updateTagLastusetime.js │ │ ├── user │ │ │ ├── createUser.js │ │ │ ├── deleteUser.js │ │ │ ├── getLoginUserInfo.js │ │ │ ├── getUserDetail.js │ │ │ ├── getUserList.js │ │ │ ├── updateLoginUserInfo.js │ │ │ └── updateUser.js │ │ ├── vote │ │ │ ├── createVote.js │ │ │ ├── deleteVote.js │ │ │ ├── getVoteDetail.js │ │ │ ├── getVoteList.js │ │ │ └── updateVote.js │ │ └── votelog │ │ │ ├── deleteVotelog.js │ │ │ ├── getVotelogList.js │ │ │ └── getVotelogStats.js │ └── blog │ │ ├── attachment │ │ └── getAttachmentList.js │ │ ├── bangumi │ │ ├── getBangumiList.js │ │ ├── getBangumiSeasonList.js │ │ └── getBangumiYearList.js │ │ ├── banner │ │ └── getBannerList.js │ │ ├── book │ │ ├── getBookList.js │ │ ├── getBooktypeList.js │ │ └── getReadingBookList.js │ │ ├── comment │ │ ├── commentRetract.js │ │ ├── createComment.js │ │ ├── getCommentList.js │ │ └── getLatestComments.js │ │ ├── commentLikeLog │ │ ├── createCommentLikeLog.js │ │ └── getPostCommentLogList.js │ │ ├── event │ │ ├── getEventDetail.js │ │ └── getEventList.js │ │ ├── game │ │ ├── getGameList.js │ │ ├── getGamePlatformList.js │ │ └── getPlayingGameList.js │ │ ├── link │ │ └── getlinkList.js │ │ ├── log │ │ ├── createLog.js │ │ └── updateLogPerformanceNavigationTiming.js │ │ ├── movie │ │ ├── getMovieList.js │ │ └── getMovieYearList.js │ │ ├── navi │ │ └── getnaviList.js │ │ ├── option │ │ └── getoptionList.js │ │ ├── post │ │ ├── getPostArchiveList.js │ │ ├── getPostDetail.js │ │ ├── getPostList.js │ │ └── updatePostViewCount.js │ │ ├── postLikeLog │ │ ├── createPostLikeLog.js │ │ └── getPostLikeLogList.js │ │ ├── sidebar │ │ └── getSidebarList.js │ │ ├── sort │ │ ├── getSortDetail.js │ │ └── getSortList.js │ │ ├── tag │ │ ├── getRandomTagList.js │ │ └── getTagDetail.js │ │ ├── trend │ │ └── getTrendPostList.js │ │ └── vote │ │ ├── getVoteDetail.js │ │ └── postVote.js ├── app.js ├── bin │ └── www ├── config │ ├── cacheData.js │ ├── globalConfig.js │ └── log4js-config.js ├── entrypoint.sh ├── front │ └── README.md ├── mongodb │ ├── index.js │ ├── models │ │ ├── albums.js │ │ ├── attachments.js │ │ ├── backups.js │ │ ├── bangumis.js │ │ ├── banners.js │ │ ├── books.js │ │ ├── booktypes.js │ │ ├── commentLikeLogs.js │ │ ├── comments.js │ │ ├── emailSendHistorys.js │ │ ├── events.js │ │ ├── eventtypes.js │ │ ├── gamePlatforms.js │ │ ├── games.js │ │ ├── links.js │ │ ├── movies.js │ │ ├── navis.js │ │ ├── options.js │ │ ├── postLikeLogs.js │ │ ├── posts.js │ │ ├── readerlogs.js │ │ ├── referrers.js │ │ ├── rsslogs.js │ │ ├── sidebars.js │ │ ├── sorts.js │ │ ├── tags.js │ │ ├── users.js │ │ ├── votelogs.js │ │ └── votes.js │ └── utils │ │ ├── albums.js │ │ ├── attachments.js │ │ ├── backups.js │ │ ├── bangumis.js │ │ ├── banners.js │ │ ├── books.js │ │ ├── booktypes.js │ │ ├── commentLikeLogs.js │ │ ├── comments.js │ │ ├── emailSendHistorys.js │ │ ├── events.js │ │ ├── eventtypes.js │ │ ├── gamePlatforms.js │ │ ├── games.js │ │ ├── links.js │ │ ├── movies.js │ │ ├── navis.js │ │ ├── options.js │ │ ├── postLikeLogs.js │ │ ├── posts.js │ │ ├── readerlogs.js │ │ ├── referrers.js │ │ ├── rsslogs.js │ │ ├── sidebars.js │ │ ├── sorts.js │ │ ├── tags.js │ │ ├── users.js │ │ ├── votelogs.js │ │ └── votes.js ├── package.json ├── public │ ├── content │ │ └── uploadfile │ │ │ └── README.md │ ├── ucloudImg │ │ └── README.md │ ├── up_works │ │ └── README.md │ ├── upload │ │ ├── avatar │ │ │ └── README.md │ │ ├── bangumi │ │ │ └── README.md │ │ ├── banner │ │ │ └── README.md │ │ ├── linkicon │ │ │ └── README.md │ │ └── siteImg │ │ │ └── README.md │ └── web_demo │ │ └── README.md ├── routes │ ├── admin.js │ ├── blog.js │ └── rss │ │ └── index.js ├── sample.env ├── seo │ ├── rss │ │ └── README.md │ └── sitemap │ │ └── sitemap.xsl ├── tools │ ├── backupFromEmlogJSON.js │ ├── backupFromEmlogJson │ │ └── README.md │ ├── cacheTest.js │ ├── changeUserPassword.js │ ├── createCRUD.js │ ├── createUser.js │ ├── deleteEmailSmtpSslOption.js │ ├── deleteOldSitePostCommonFooterContent.js │ ├── fixAttachmentMimetype.js │ ├── fixMongoDbDefaultValues.js │ ├── fixNavisQuery.js │ ├── fixPostLastChangDate.js │ ├── getUserList.js │ ├── mongodb.js │ ├── setUserBan.js │ └── sumPostComment.js ├── utils │ ├── backup.js │ ├── chalk.js │ ├── crawler-user-agents.json │ ├── ip2location │ │ └── README.md │ ├── rss.js │ ├── sitemap.js │ ├── utils.js │ └── workers │ │ ├── backupWorker.js │ │ ├── restoreWorker.js │ │ └── sharpWorker.js └── yarn.lock ├── ver-change.js └── yarn.lock /.dockerignore: -------------------------------------------------------------------------------- 1 | # compiled output 2 | dist 3 | node_modules 4 | 5 | # Logs 6 | logs 7 | log 8 | *.log 9 | npm-debug.log* 10 | yarn-debug.log* 11 | yarn-error.log* 12 | lerna-debug.log* 13 | 14 | # OS 15 | .DS_Store 16 | 17 | # Tests 18 | /coverage 19 | /.nyc_output 20 | 21 | # IDEs and editors 22 | /.idea 23 | .project 24 | .classpath 25 | .c9/ 26 | *.launch 27 | .settings/ 28 | *.sublime-workspace 29 | 30 | # IDE - VSCode 31 | .vscode/* 32 | !.vscode/settings.json 33 | !.vscode/tasks.json 34 | !.vscode/launch.json 35 | !.vscode/extensions.json 36 | 37 | patch/dist 38 | tmp 39 | out 40 | release.zip 41 | 42 | run 43 | 44 | data 45 | assets 46 | .env -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # dependencies 2 | node_modules 3 | .env 4 | data 5 | /server/log 6 | /server/public/upload/avatar/* 7 | /server/public/upload/linkicon/* 8 | /server/public/upload/banner/* 9 | /server/public/upload/siteImg/* 10 | /server/public/upload/bangumi/* 11 | /server/public/upload/movie/* 12 | /server/public/upload/gameCover/* 13 | /server/public/upload/bookCover/* 14 | /server/public/content/uploadfile/* 15 | /server/public/admin/* 16 | /server/public/up_works/* 17 | /server/public/web_demo/* 18 | /server/public/ucloudImg/* 19 | /server/seo/sitemap/sitemap.xml 20 | /server/seo/rss/* 21 | /server/front/* 22 | /server/config/globalConfig.json 23 | /server/uploadCache/* 24 | /server/utils/ip2location/* 25 | /server/tools/backupFromEmlogJson/* 26 | /server/cache/* 27 | /server/secret/* 28 | /server/backups/* 29 | /blog/public/ads.txt 30 | /blog/public/robots.txt -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "css.lint.unknownAtRules": "ignore" 3 | } 4 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:20-alpine AS builder 2 | WORKDIR /app 3 | COPY . . 4 | RUN cd admin && \ 5 | yarn install && \ 6 | yarn build && \ 7 | cd ../server && \ 8 | yarn install 9 | 10 | FROM node:20-alpine AS runner 11 | WORKDIR /app 12 | COPY --from=builder /app/server /app 13 | RUN chmod +x /app/entrypoint.sh && \ 14 | apk add --no-cache bash 15 | CMD ["/app/entrypoint.sh"] 16 | EXPOSE 3006 -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 广树 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /admin/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | 6 | # local env files 7 | .env.local 8 | .env.*.local 9 | 10 | # Log files 11 | npm-debug.log* 12 | yarn-debug.log* 13 | yarn-error.log* 14 | pnpm-debug.log* 15 | 16 | # Editor directories and files 17 | .idea 18 | .vscode 19 | *.suo 20 | *.ntvs* 21 | *.njsproj 22 | *.sln 23 | *.sw? 24 | -------------------------------------------------------------------------------- /admin/README.md: -------------------------------------------------------------------------------- 1 | # admin 2 | 3 | ## Project setup 4 | ``` 5 | npm install 6 | ``` 7 | 8 | ### Compiles and hot-reloads for development 9 | ``` 10 | npm run serve 11 | ``` 12 | 13 | ### Compiles and minifies for production 14 | ``` 15 | npm run build 16 | ``` 17 | 18 | ### Customize configuration 19 | See [Configuration Reference](https://cli.vuejs.org/config/). 20 | -------------------------------------------------------------------------------- /admin/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/cli-plugin-babel/preset' 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /admin/jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "esnext", 5 | "baseUrl": "./", 6 | "moduleResolution": "node", 7 | "paths": { 8 | "@/*": [ 9 | "src/*" 10 | ] 11 | }, 12 | "lib": [ 13 | "esnext", 14 | "dom", 15 | "dom.iterable", 16 | "scripthost" 17 | ] 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /admin/public/awesome/webfonts/fa-brands-400.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/admin/public/awesome/webfonts/fa-brands-400.eot -------------------------------------------------------------------------------- /admin/public/awesome/webfonts/fa-brands-400.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/admin/public/awesome/webfonts/fa-brands-400.ttf -------------------------------------------------------------------------------- /admin/public/awesome/webfonts/fa-brands-400.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/admin/public/awesome/webfonts/fa-brands-400.woff -------------------------------------------------------------------------------- /admin/public/awesome/webfonts/fa-brands-400.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/admin/public/awesome/webfonts/fa-brands-400.woff2 -------------------------------------------------------------------------------- /admin/public/awesome/webfonts/fa-regular-400.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/admin/public/awesome/webfonts/fa-regular-400.eot -------------------------------------------------------------------------------- /admin/public/awesome/webfonts/fa-regular-400.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/admin/public/awesome/webfonts/fa-regular-400.ttf -------------------------------------------------------------------------------- /admin/public/awesome/webfonts/fa-regular-400.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/admin/public/awesome/webfonts/fa-regular-400.woff -------------------------------------------------------------------------------- /admin/public/awesome/webfonts/fa-regular-400.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/admin/public/awesome/webfonts/fa-regular-400.woff2 -------------------------------------------------------------------------------- /admin/public/awesome/webfonts/fa-solid-900.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/admin/public/awesome/webfonts/fa-solid-900.eot -------------------------------------------------------------------------------- /admin/public/awesome/webfonts/fa-solid-900.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/admin/public/awesome/webfonts/fa-solid-900.ttf -------------------------------------------------------------------------------- /admin/public/awesome/webfonts/fa-solid-900.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/admin/public/awesome/webfonts/fa-solid-900.woff -------------------------------------------------------------------------------- /admin/public/awesome/webfonts/fa-solid-900.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/admin/public/awesome/webfonts/fa-solid-900.woff2 -------------------------------------------------------------------------------- /admin/public/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/admin/public/favicon.png -------------------------------------------------------------------------------- /admin/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 博客管理后台 10 | 11 | 12 | 19 |
20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /admin/src/App.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 8 | -------------------------------------------------------------------------------- /admin/src/api/create-api.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | 3 | export function createAPI(config) { 4 | return axios.create(config) 5 | } 6 | -------------------------------------------------------------------------------- /admin/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/admin/src/assets/logo.png -------------------------------------------------------------------------------- /admin/src/components/CheckOption.vue: -------------------------------------------------------------------------------- 1 | 7 | 31 | 38 | -------------------------------------------------------------------------------- /admin/src/components/IpInfoDisplay.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 24 | -------------------------------------------------------------------------------- /admin/src/services/CheckDialogService.js: -------------------------------------------------------------------------------- 1 | import { h, render } from 'vue' 2 | import CheckDialog from '@/components/CheckDialog.vue' 3 | 4 | class CheckDialogService { 5 | static open (props) { 6 | return new Promise((resolve, reject) => { 7 | let container = document.createElement('div') 8 | document.body.appendChild(container) 9 | 10 | const destroy = () => { 11 | if (!container) return 12 | render(null, container) 13 | document.body.removeChild(container) 14 | container = null 15 | } 16 | 17 | const close = () => { 18 | vNode.component.props.isOpen = false 19 | } 20 | 21 | const vNode = h(CheckDialog, { 22 | ...props, 23 | isOpen: true, 24 | onConfirm: () => { 25 | resolve() 26 | close() 27 | }, 28 | 'onUpdate:isOpen': (value) => { 29 | if (!value) { 30 | console.log(vNode) 31 | close() 32 | reject() 33 | } 34 | }, 35 | onClosed: destroy 36 | }) 37 | 38 | render(vNode, container) 39 | }) 40 | } 41 | } 42 | 43 | export default CheckDialogService -------------------------------------------------------------------------------- /admin/src/utils/editorMenu/event/elem-to-html.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @description elem to html 3 | */ 4 | 5 | // 生成 html 的函数 6 | function eventspanToHtml (elem, childrenHtml) { 7 | const { id = '', textContent = '' } = elem 8 | return `${textContent}` 9 | } 10 | 11 | // 配置 12 | const conf = { 13 | type: 'eventspan', // 节点 type ,重要!!! 14 | elemToHtml: eventspanToHtml, 15 | } 16 | 17 | export default conf -------------------------------------------------------------------------------- /admin/src/utils/editorMenu/event/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @description eventspan module entry 3 | */ 4 | 5 | 6 | import withEventspan from './plugin' 7 | import renderElemConf from './render-elem' 8 | import elemToHtmlConf from './elem-to-html' 9 | import parseHtmlConf from './parse-elem-html' 10 | import menuConf from './menu/menu' 11 | import unEventspanConf from './menu/unsetEventspan' 12 | import editeventspanConf from './menu/editEventspan' 13 | 14 | const module = { 15 | editorPlugin: withEventspan, 16 | renderElems: [renderElemConf], 17 | elemsToHtml: [elemToHtmlConf], 18 | parseElemsHtml: [parseHtmlConf], 19 | menus: [menuConf, unEventspanConf, editeventspanConf], 20 | } 21 | 22 | export default module -------------------------------------------------------------------------------- /admin/src/utils/editorMenu/event/parse-elem-html.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @description parse elem html 3 | */ 4 | import { Text } from 'slate' 5 | 6 | function parseHtml (elem, children, editor) { 7 | // children = children.filter(child => { 8 | // if (Text.isText(child)) return true 9 | // if (editor.isInline(child)) return true 10 | // return false 11 | // }) 12 | 13 | // // 无 children ,则用纯文本 14 | // if (children.length === 0) { 15 | // children = [{ text: elem.textContent.replace(/\s+/gm, ' ') }] 16 | // } 17 | const id = elem.getAttribute('data-id') || '' 18 | const textContent = elem.textContent || '' 19 | return { 20 | type: 'eventspan', 21 | id: id, 22 | textContent: textContent, 23 | children: [{ text: textContent }], // void node 必须有一个空白 text 24 | } 25 | } 26 | 27 | const parseHtmlConf = { 28 | selector: 'span[data-w-e-type="eventspan"]', 29 | parseElemHtml: parseHtml, 30 | } 31 | 32 | export default parseHtmlConf -------------------------------------------------------------------------------- /admin/src/utils/editorMenu/event/plugin.js: -------------------------------------------------------------------------------- 1 | import { DomEditor } from '@wangeditor/editor' 2 | import { Editor, Node, Transforms } from 'slate' 3 | function withEventspan (editor) { 4 | const { isInline, isVoid, normalizeNode } = editor 5 | const newEditor = editor 6 | 7 | newEditor.isInline = elem => { 8 | const type = DomEditor.getNodeType(elem) 9 | if (type === 'eventspan') return true // 针对 type: eventspan ,设置为 inline 10 | return isInline(elem) 11 | } 12 | 13 | newEditor.isVoid = elem => { 14 | const type = DomEditor.getNodeType(elem) 15 | if (type === 'eventspan') return true // 针对 type: eventspan ,设置为 void 16 | return isVoid(elem) 17 | } 18 | 19 | // newEditor.normalizeNode = ([node, path]) => { 20 | // const type = DomEditor.getNodeType(node) 21 | // if (type !== 'eventspan') { 22 | // // 未命中 ,执行默认的 normalizeNode 23 | // return normalizeNode([node, path]) 24 | // } 25 | 26 | // // 如果链接内容为空,则删除 27 | // const str = Node.string(node) 28 | // if (str === '') { 29 | // return Transforms.removeNodes(newEditor, { at: path }) 30 | // } 31 | // return normalizeNode([node, path]) 32 | // } 33 | 34 | return newEditor // 返回 newEditor ,重要!!! 35 | } 36 | 37 | export default withEventspan -------------------------------------------------------------------------------- /admin/src/utils/editorMenu/event/render-elem.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @description render elem 3 | */ 4 | 5 | import { h } from 'snabbdom' 6 | 7 | function renderEventspan (elem, children, editor, info) { 8 | // 当前节点是否选中 9 | console.log(elem) 10 | const vnode = h( 11 | 'span', 12 | { 13 | style: { 14 | cursor: 'pointer', 15 | color: 'var(--el-color-primary)', 16 | }, 17 | }, 18 | [{ text: elem.textContent }] 19 | ) 20 | 21 | return vnode 22 | } 23 | 24 | const conf = { 25 | type: 'eventspan', // 节点 type ,重要!!! 26 | renderElem: renderEventspan, 27 | } 28 | 29 | export default conf -------------------------------------------------------------------------------- /admin/src/utils/editorMenu/image/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @description eventspan module entry 3 | */ 4 | 5 | import imageWidth33Conf from './menu/menu33' 6 | import imageWidthAutoConf from './menu/menuauto' 7 | 8 | const module = { 9 | menus: [imageWidth33Conf, imageWidthAutoConf], 10 | } 11 | 12 | export default module -------------------------------------------------------------------------------- /admin/src/utils/editorMenu/imageGroup/elem-to-html.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @description elem to html 3 | */ 4 | 5 | // 生成 html 的函数 6 | function imageGroupToHtml (elem, childrenHtml) { 7 | const { childrenList } = elem 8 | let className = 'w-e-image-group' 9 | 10 | if (childrenList.length % 2 === 0) { 11 | className += ' w-e-image-group-even' 12 | } else { 13 | className += ' w-e-image-group-odd' 14 | } 15 | 16 | let html = `
` 17 | // 遍历子节点, 生成 img 标签 18 | childrenList.forEach((child, index) => { 19 | html += `
${child.alt}
` 20 | }) 21 | html += '
' 22 | return html 23 | } 24 | 25 | // 配置 26 | const conf = { 27 | type: 'imageGroup', // 节点 type ,重要!!! 28 | elemToHtml: imageGroupToHtml, 29 | } 30 | 31 | export default conf -------------------------------------------------------------------------------- /admin/src/utils/editorMenu/imageGroup/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @description imageGroup module entry 3 | */ 4 | 5 | 6 | import withImageGroup from './plugin' 7 | import renderElemConf from './render-elem' 8 | import elemToHtmlConf from './elem-to-html' 9 | import parseHtmlConf from './parse-elem-html' 10 | import menuConf from './menu/menu' 11 | 12 | const module = { 13 | editorPlugin: withImageGroup, 14 | renderElems: [renderElemConf], 15 | elemsToHtml: [elemToHtmlConf], 16 | parseElemsHtml: [parseHtmlConf], 17 | menus: [menuConf], 18 | } 19 | 20 | export default module -------------------------------------------------------------------------------- /admin/src/utils/editorMenu/imageGroup/plugin.js: -------------------------------------------------------------------------------- 1 | import { DomEditor } from '@wangeditor/editor' 2 | import { Editor, Node, Transforms } from 'slate' 3 | function withImageGroup (editor) { 4 | const { isInline, isVoid, normalizeNode } = editor 5 | const newEditor = editor 6 | 7 | 8 | newEditor.isVoid = elem => { 9 | const type = DomEditor.getNodeType(elem) 10 | if (type === 'imageGroup') return true // 针对 type: imageGroup ,设置为 void 11 | return isVoid(elem) 12 | } 13 | 14 | newEditor.normalizeNode = ([node, path]) => { 15 | const type = DomEditor.getNodeType(node) 16 | if (type === 'imageGroup') { 17 | const isLast = DomEditor.isLastNode(newEditor, node) 18 | if (isLast) { 19 | Transforms.insertNodes(newEditor, DomEditor.genEmptyParagraph(), { at: [path[0] + 1] }) 20 | } 21 | } 22 | 23 | // // 如果链接内容为空,则删除 24 | // const str = Node.string(node) 25 | // if (str === '') { 26 | // return Transforms.removeNodes(newEditor, { at: path }) 27 | // } 28 | return normalizeNode([node, path]) 29 | } 30 | 31 | return newEditor // 返回 newEditor ,重要!!! 32 | } 33 | 34 | export default withImageGroup -------------------------------------------------------------------------------- /admin/src/utils/editorMenu/panorama360/elem-to-html.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @description elem to html 3 | */ 4 | 5 | function panorama360ToHtml (elem, childrenHtml) { 6 | const { src, width, height, dataHref, dataHrefWidth, dataHrefHeight, alt } = elem 7 | 8 | return `
9 | ${alt || '360°全景图片'} 10 |
` 11 | } 12 | 13 | // 配置 14 | const conf = { 15 | type: 'panorama360', // 节点 type ,重要!!! 16 | elemToHtml: panorama360ToHtml, 17 | } 18 | 19 | export default conf -------------------------------------------------------------------------------- /admin/src/utils/editorMenu/panorama360/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @description panorama360 module entry 3 | */ 4 | 5 | import withPanorama360 from './plugin' 6 | import renderElemConf from './render-elem' 7 | import elemToHtmlConf from './elem-to-html' 8 | import parsePanorama360HtmlConf from './parse-elem-html' 9 | import menuConf from './menu/menu' 10 | 11 | const module = { 12 | editorPlugin: withPanorama360, 13 | renderElems: [renderElemConf], 14 | elemsToHtml: [elemToHtmlConf], 15 | parseElemsHtml: [parsePanorama360HtmlConf()], 16 | menus: [menuConf], 17 | } 18 | 19 | export default module -------------------------------------------------------------------------------- /admin/src/utils/editorMenu/panorama360/parse-elem-html.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @description parse elem html 3 | */ 4 | 5 | function parseHtml (domElem, children, editor) { 6 | const imgElem = domElem.querySelector('img.w-e-panorama360-img') 7 | if (imgElem == null) return 8 | 9 | const src = imgElem.getAttribute('src') 10 | const width = imgElem.getAttribute('width') 11 | const height = imgElem.getAttribute('height') 12 | const dataHref = imgElem.getAttribute('data-href') 13 | const dataHrefWidth = imgElem.getAttribute('data-href-width') 14 | const dataHrefHeight = imgElem.getAttribute('data-href-height') 15 | const alt = imgElem.getAttribute('alt') 16 | 17 | return { 18 | type: 'panorama360', 19 | src, 20 | width, 21 | height, 22 | dataHref, 23 | dataHrefWidth, 24 | dataHrefHeight, 25 | alt, 26 | children: [{ text: '' }], 27 | } 28 | } 29 | 30 | function parsePanorama360HtmlConf () { 31 | return { 32 | selector: 'div.w-e-panorama360', 33 | parseElemHtml: parseHtml 34 | } 35 | } 36 | 37 | export default parsePanorama360HtmlConf -------------------------------------------------------------------------------- /admin/src/utils/editorMenu/panorama360/plugin.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @description panorama360 plugin 3 | */ 4 | 5 | import { DomEditor } from '@wangeditor/core' 6 | import { Transforms } from 'slate' 7 | 8 | function withPanorama360 (editor) { 9 | const { isInline, isVoid, normalizeNode } = editor 10 | const newEditor = editor 11 | 12 | // 重写 isInline 13 | newEditor.isInline = elem => { 14 | const type = DomEditor.getNodeType(elem) 15 | if (type === 'panorama360') return false 16 | return isInline(elem) 17 | } 18 | 19 | // 重写 isVoid 20 | newEditor.isVoid = elem => { 21 | const type = DomEditor.getNodeType(elem) 22 | if (type === 'panorama360') return true 23 | return isVoid(elem) 24 | } 25 | 26 | newEditor.normalizeNode = ([node, path]) => { 27 | const type = DomEditor.getNodeType(node) 28 | if (type === 'panorama360') { 29 | const isLast = DomEditor.isLastNode(newEditor, node) 30 | if (isLast) { 31 | Transforms.insertNodes(newEditor, DomEditor.genEmptyParagraph(), { at: [path[0] + 1] }) 32 | } 33 | } 34 | return normalizeNode([node, path]) 35 | } 36 | 37 | return newEditor // 返回 newEditor ,重要!!! 38 | } 39 | 40 | export default withPanorama360 -------------------------------------------------------------------------------- /admin/src/utils/editorMenu/panorama360/render-elem.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @description render elem 3 | */ 4 | 5 | import { h } from 'snabbdom' 6 | import { DomEditor } from '@wangeditor/core' 7 | 8 | function renderPanorama360 (elem, children, editor, info) { 9 | // 当前节点是否选中 10 | const selected = DomEditor.isNodeSelected(editor, elem) 11 | let className = 'w-e-panorama360' 12 | if (selected) { 13 | className += ' w-e-selected' 14 | } 15 | 16 | const vnode = h( 17 | 'div', 18 | { 19 | className: className, 20 | }, 21 | [h( 22 | 'img', 23 | { 24 | className: 'w-e-panorama360-img', 25 | src: elem.src, 26 | width: elem.width || '100%', 27 | height: elem.height || '400px', 28 | 'data-href': elem.dataHref || '', 29 | 'data-href-width': elem.dataHrefWidth || '', 30 | 'data-href-height': elem.dataHrefHeight || '', 31 | alt: elem.alt || '360°全景图片', 32 | 'data-type': 'panorama360', 33 | } 34 | )] 35 | ) 36 | 37 | return vnode 38 | } 39 | 40 | const conf = { 41 | type: 'panorama360', // 节点 type ,重要!!! 42 | renderElem: renderPanorama360, 43 | } 44 | 45 | export default conf -------------------------------------------------------------------------------- /admin/src/utils/editorMenu/utils.js: -------------------------------------------------------------------------------- 1 | import { Range, Editor } from 'slate' 2 | import { DomEditor } from '@wangeditor/core' 3 | 4 | export function isInsertSpanDisabled (editor) { 5 | const { selection } = editor 6 | if (selection == null) return true 7 | // if (!Range.isCollapsed(selection)) return true // 选区非折叠,禁用 8 | 9 | const [match] = Editor.nodes(editor, { 10 | match: n => { 11 | const type = DomEditor.getNodeType(n) 12 | 13 | if (type === 'code') return true // 代码块 14 | if (type === 'pre') return true // 代码块 15 | if (type === 'link') return true // 链接 16 | if (type === 'blockquote') return true // 引用 17 | if (type === 'image') return true // 图片 18 | if (type === 'video') return true // 视频 19 | if (type === 'eventspan') return true // 活动 20 | if (Editor.isVoid(editor, n)) return true // void 21 | 22 | return false 23 | }, 24 | universal: true, 25 | }) 26 | 27 | if (match) return true 28 | return false 29 | } -------------------------------------------------------------------------------- /admin/src/utils/theme.js: -------------------------------------------------------------------------------- 1 | export const applyThemeToDom = (newTheme) => { 2 | const validThemes = ['light', 'dark']; 3 | if (!validThemes.includes(newTheme)) return; 4 | document.documentElement.setAttribute('data-theme', newTheme); 5 | if (newTheme === 'dark') { 6 | document.documentElement.classList.add('dark'); 7 | document.documentElement.classList.remove('light'); 8 | } else { 9 | document.documentElement.classList.add('light'); 10 | document.documentElement.classList.remove('dark'); 11 | } 12 | }; -------------------------------------------------------------------------------- /blog/.gitignore: -------------------------------------------------------------------------------- 1 | # Nuxt dev/build outputs 2 | .output 3 | .data 4 | .nuxt 5 | .nitro 6 | .cache 7 | dist 8 | 9 | # Node dependencies 10 | node_modules 11 | 12 | # Logs 13 | logs 14 | *.log 15 | 16 | # Misc 17 | .DS_Store 18 | .fleet 19 | .idea 20 | 21 | # Local env files 22 | .env 23 | .env.* 24 | !.env.example 25 | -------------------------------------------------------------------------------- /blog/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:20-alpine AS builder 2 | 3 | WORKDIR /app 4 | COPY . . 5 | ARG NUXT_API_DOMAIN=http://wikimoe-blog-server:3006 6 | ARG SWR_ENABLED=0 7 | ARG SWR_CACHE_MAXAGE 8 | ARG SWR_CACHE_STALEMAXAGE 9 | ARG CACHE_MAX_PAGE 10 | ARG CACHE_TTL 11 | # env debug info 12 | RUN node -e "console.log(process.env)" && \ 13 | yarn install && \ 14 | yarn build 15 | 16 | 17 | FROM node:20-alpine AS runner 18 | 19 | WORKDIR /app 20 | COPY --from=builder /app/build /app 21 | CMD ["yarn", "run","start-linux"] 22 | EXPOSE 3007 -------------------------------------------------------------------------------- /blog/README.md: -------------------------------------------------------------------------------- 1 | # Nuxt 3 Minimal Starter 2 | 3 | Look at the [Nuxt 3 documentation](https://nuxt.com/docs/getting-started/introduction) to learn more. 4 | 5 | ## Setup 6 | 7 | Make sure to install the dependencies: 8 | 9 | ```bash 10 | # npm 11 | npm install 12 | 13 | # pnpm 14 | pnpm install 15 | 16 | # yarn 17 | yarn install 18 | 19 | # bun 20 | bun install 21 | ``` 22 | 23 | ## Development Server 24 | 25 | Start the development server on `http://localhost:3000`: 26 | 27 | ```bash 28 | # npm 29 | npm run dev 30 | 31 | # pnpm 32 | pnpm run dev 33 | 34 | # yarn 35 | yarn dev 36 | 37 | # bun 38 | bun run dev 39 | ``` 40 | 41 | ## Production 42 | 43 | Build the application for production: 44 | 45 | ```bash 46 | # npm 47 | npm run build 48 | 49 | # pnpm 50 | pnpm run build 51 | 52 | # yarn 53 | yarn build 54 | 55 | # bun 56 | bun run build 57 | ``` 58 | 59 | Locally preview production build: 60 | 61 | ```bash 62 | # npm 63 | npm run preview 64 | 65 | # pnpm 66 | pnpm run preview 67 | 68 | # yarn 69 | yarn preview 70 | 71 | # bun 72 | bun run preview 73 | ``` 74 | 75 | Check out the [deployment documentation](https://nuxt.com/docs/getting-started/deployment) for more information. 76 | -------------------------------------------------------------------------------- /blog/api/attachment.ts: -------------------------------------------------------------------------------- 1 | // banner 2 | import httpRequest from '~/api' 3 | 4 | /** 5 | * @description 查询配置项 6 | * @return {any} 返回配置项 7 | */ 8 | 9 | const URL = `/attachment/list` 10 | const getAttachmentListApiFetch = (params: any) => { 11 | return httpRequest.getFetch(URL, params) 12 | } 13 | 14 | export { getAttachmentListApiFetch } 15 | -------------------------------------------------------------------------------- /blog/api/bangumi.ts: -------------------------------------------------------------------------------- 1 | // banner 2 | import httpRequest from '~/api' 3 | 4 | /** 5 | * @description 查询配置项 6 | * @return {any} 返回配置项 7 | */ 8 | 9 | const URL = `/bangumi/list` 10 | const getBangumiListApi = (params: any) => { 11 | return httpRequest.get(URL, params, { watch: false }) 12 | } 13 | const getBangumiListApiFetch = (params: any) => { 14 | return httpRequest.getFetch(URL, params) 15 | } 16 | 17 | // /bangumi/year/list 18 | const URL2 = `/bangumi/year/list` 19 | const getBangumiYearListApi = (params: any) => { 20 | return httpRequest.get(URL2, { watch: false }) 21 | } 22 | 23 | // /bangumi/season/list 24 | const URL3 = `/bangumi/season/list` 25 | const getBangumiSeasonListApi = (params: any) => { 26 | return httpRequest.get(URL3, { watch: false }) 27 | } 28 | 29 | export { 30 | getBangumiListApi, 31 | getBangumiListApiFetch, 32 | getBangumiYearListApi, 33 | getBangumiSeasonListApi, 34 | } 35 | -------------------------------------------------------------------------------- /blog/api/banner.ts: -------------------------------------------------------------------------------- 1 | // banner 2 | import httpRequest from '~/api' 3 | 4 | /** 5 | * @description 查询配置项 6 | * @return {any} 返回配置项 7 | */ 8 | 9 | const URL = `/banner/list` 10 | const getBannerListApi = () => { 11 | return httpRequest.get(URL) 12 | } 13 | 14 | export { getBannerListApi } 15 | -------------------------------------------------------------------------------- /blog/api/book.ts: -------------------------------------------------------------------------------- 1 | // banner 2 | import httpRequest from '~/api' 3 | 4 | /** 5 | * @description 查询配置项 6 | * @return {any} 返回配置项 7 | */ 8 | 9 | const URL = `/book/list` 10 | const getBookListApi = (params: any) => { 11 | return httpRequest.get(URL, params, { watch: false }) 12 | } 13 | const getBookListApiFetch = (params: any) => { 14 | return httpRequest.getFetch(URL, params) 15 | } 16 | 17 | const URL2 = `/booktype/list` 18 | const getBooktypeListApi = (params: any) => { 19 | return httpRequest.get(URL2, params, { watch: false }) 20 | } 21 | 22 | // /book/reading/list 23 | const URL3 = `/book/reading/list` 24 | const getBookReadingListApi = (params: any) => { 25 | return httpRequest.get(URL3, { watch: false }) 26 | } 27 | 28 | export { 29 | getBookListApi, 30 | getBookListApiFetch, 31 | getBooktypeListApi, 32 | getBookReadingListApi, 33 | } 34 | -------------------------------------------------------------------------------- /blog/api/event.ts: -------------------------------------------------------------------------------- 1 | // banner 2 | import httpRequest from '~/api' 3 | 4 | /** 5 | * @description 查询配置项 6 | * @return {any} 返回配置项 7 | */ 8 | 9 | const URL = `/event/list` 10 | const getEventListApiFetch = (params: any) => { 11 | return httpRequest.getFetch(URL, params) 12 | } 13 | 14 | // /event/detail 15 | const URL_DETAIL = `/event/detail` 16 | const getEventDetailApiFetch = (params: any) => { 17 | return httpRequest.getFetch(URL_DETAIL, params) 18 | } 19 | 20 | export { getEventListApiFetch, getEventDetailApiFetch } 21 | -------------------------------------------------------------------------------- /blog/api/game.ts: -------------------------------------------------------------------------------- 1 | // banner 2 | import httpRequest from '~/api' 3 | 4 | /** 5 | * @description 查询配置项 6 | * @return {any} 返回配置项 7 | */ 8 | 9 | const URL = `/game/list` 10 | const getGameListApi = (params: any) => { 11 | return httpRequest.get(URL, params, { watch: false }) 12 | } 13 | const getGameListApiFetch = (params: any) => { 14 | return httpRequest.getFetch(URL, params) 15 | } 16 | 17 | const URL2 = `/game/platform/list` 18 | const getGamePlatformListApi = (params: any) => { 19 | return httpRequest.get(URL2, params, { watch: false }) 20 | } 21 | 22 | // /game/playing/list 23 | const URL3 = `/game/playing/list` 24 | const getGamePlayingListApi = (params: any) => { 25 | return httpRequest.get(URL3, { watch: false }) 26 | } 27 | 28 | export { 29 | getGameListApi, 30 | getGameListApiFetch, 31 | getGamePlatformListApi, 32 | getGamePlayingListApi, 33 | } 34 | -------------------------------------------------------------------------------- /blog/api/link.ts: -------------------------------------------------------------------------------- 1 | import httpRequest from '~/api' 2 | 3 | /** 4 | * @description 查询配置项 5 | * @return {any} 返回配置项 6 | */ 7 | 8 | const URL = `/link/list` 9 | const getLinkListApi = () => { 10 | return httpRequest.get(URL) 11 | } 12 | 13 | export { getLinkListApi } 14 | -------------------------------------------------------------------------------- /blog/api/log.ts: -------------------------------------------------------------------------------- 1 | import httpRequest from '~/api' 2 | 3 | // post '/log/create' 4 | const createURL = `/log/create` 5 | const postLogCreateApi = (params: any) => { 6 | return httpRequest.postFetch(`${createURL}`, params, { 7 | shouldUuid: true, 8 | }) 9 | } 10 | 11 | // put /log/update/performance 12 | const updatePerformanceURL = `/log/update/performance` 13 | const putLogUpdatePerformanceApi = (params: any) => { 14 | return httpRequest.putFetch(`${updatePerformanceURL}`, params, { 15 | shouldUuid: true, 16 | }) 17 | } 18 | 19 | export { postLogCreateApi, putLogUpdatePerformanceApi } 20 | -------------------------------------------------------------------------------- /blog/api/movie.ts: -------------------------------------------------------------------------------- 1 | // banner 2 | import httpRequest from '~/api' 3 | 4 | /** 5 | * @description 查询配置项 6 | * @return {any} 返回配置项 7 | */ 8 | 9 | const URL = `/movie/list` 10 | const getMovieListApi = (params: any) => { 11 | return httpRequest.get(URL, params, { watch: false }) 12 | } 13 | const getMovieListApiFetch = (params: any) => { 14 | return httpRequest.getFetch(URL, params) 15 | } 16 | 17 | // /movie/year/list 18 | const URL_YEAR = `/movie/year/list` 19 | const getMovieYearListApi = (params: any) => { 20 | return httpRequest.get(URL_YEAR, params, { watch: false }) 21 | } 22 | 23 | export { getMovieListApi, getMovieListApiFetch, getMovieYearListApi } 24 | -------------------------------------------------------------------------------- /blog/api/navi.ts: -------------------------------------------------------------------------------- 1 | import httpRequest from '~/api' 2 | 3 | /** 4 | * @description 查询配置项 5 | * @return {any} 返回配置项 6 | */ 7 | 8 | const URL = `/navi/list` 9 | const getNaviListApi = () => { 10 | return httpRequest.get(URL) 11 | } 12 | 13 | export { getNaviListApi } 14 | -------------------------------------------------------------------------------- /blog/api/option.ts: -------------------------------------------------------------------------------- 1 | import httpRequest from '~/api' 2 | 3 | /** 4 | * @description 查询配置项 5 | * @return {any} 返回配置项 6 | */ 7 | 8 | const URL = `/options` 9 | const getOptionsApi = () => { 10 | return httpRequest.get(URL) 11 | } 12 | 13 | export { getOptionsApi } 14 | -------------------------------------------------------------------------------- /blog/api/sidebar.ts: -------------------------------------------------------------------------------- 1 | // banner 2 | import httpRequest from '~/api' 3 | 4 | /** 5 | * @description 查询配置项 6 | * @return {any} 返回配置项 7 | */ 8 | 9 | const URL = `/sidebar/list` 10 | const getSidebarListApi = () => { 11 | return httpRequest.get(URL) 12 | } 13 | 14 | export { getSidebarListApi } 15 | -------------------------------------------------------------------------------- /blog/api/sort.ts: -------------------------------------------------------------------------------- 1 | // banner 2 | import httpRequest from '~/api' 3 | 4 | /** 5 | * @description 查询配置项 6 | * @return {any} 返回配置项 7 | */ 8 | 9 | const URL = `/sort/list` 10 | const getSortListApi = () => { 11 | return httpRequest.get(URL) 12 | } 13 | // /sort/detail 14 | const URL_DETAIL = `/sort/detail` 15 | const getSortDetailApi = (params: any) => { 16 | return httpRequest.get(URL_DETAIL, params) 17 | } 18 | 19 | export { getSortListApi, getSortDetailApi } 20 | -------------------------------------------------------------------------------- /blog/api/tag.ts: -------------------------------------------------------------------------------- 1 | // banner 2 | import httpRequest from '~/api' 3 | 4 | /** 5 | * @description 查询配置项 6 | * @return {any} 返回配置项 7 | */ 8 | 9 | const URL = `/tag/detail` 10 | const getTagDetailApi = (params: any) => { 11 | return httpRequest.get(URL, params) 12 | } 13 | 14 | // /tag/random/list 15 | const RandomURL = `/tag/random/list` 16 | const getRandomTagListApi = (params: any) => { 17 | return httpRequest.get(RandomURL, params) 18 | } 19 | 20 | export { getTagDetailApi, getRandomTagListApi } 21 | -------------------------------------------------------------------------------- /blog/api/trend.ts: -------------------------------------------------------------------------------- 1 | // banner 2 | import httpRequest from '~/api' 3 | 4 | /** 5 | * @description 查询配置项 6 | * @return {any} 返回配置项 7 | */ 8 | 9 | const POSTURL = `/trend/post/list` 10 | const getTrendPostListApi = (params: any) => { 11 | return httpRequest.get(POSTURL, params) 12 | } 13 | 14 | export { getTrendPostListApi } 15 | -------------------------------------------------------------------------------- /blog/api/vote.ts: -------------------------------------------------------------------------------- 1 | // banner 2 | import httpRequest from '~/api' 3 | 4 | /** 5 | * @description 查询配置项 6 | * @return {any} 返回配置项 7 | */ 8 | 9 | // get /vote/detail 10 | const voteDetailURL = `/vote/detail` 11 | const getVoteDetailApi = (params: any) => { 12 | return httpRequest.getFetch(`${voteDetailURL}`, params, { 13 | shouldUuid: true, 14 | }) 15 | } 16 | 17 | // post /vote 18 | const voteURL = `/vote` 19 | const postVoteApi = (params: any) => { 20 | return httpRequest.postFetch(`${voteURL}`, params, { 21 | shouldUuid: true, 22 | }) 23 | } 24 | 25 | export { getVoteDetailApi, postVoteApi } 26 | -------------------------------------------------------------------------------- /blog/app.config.ts: -------------------------------------------------------------------------------- 1 | export default defineAppConfig({ 2 | ui: { 3 | primary: 'wikimoe', 4 | notifications: { 5 | // Show toasts at the top right of the screen 6 | position: 'top-0 bottom-auto', 7 | }, 8 | popover: { 9 | container: 'z-10 group', 10 | }, 11 | }, 12 | }) 13 | -------------------------------------------------------------------------------- /blog/build/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "start-linux": "NITRO_PORT=3007 node .output/server/index.mjs", 4 | "start-linux-with-jemalloc": "env LD_PRELOAD=/usr/local/lib/libjemalloc.so.2 NITRO_PORT=3007 node .output/server/index.mjs", 5 | "start-windows": "set NITRO_PORT=3007 && node .output/server/index.mjs" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /blog/components/Adsbygoogle.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /blog/components/AdsbygoogleHave.vue: -------------------------------------------------------------------------------- 1 | 10 | 45 | -------------------------------------------------------------------------------- /blog/components/BangumiSeasonList.vue: -------------------------------------------------------------------------------- 1 | 20 | 25 | 26 | -------------------------------------------------------------------------------- /blog/components/DivLoading.vue: -------------------------------------------------------------------------------- 1 | 15 | 27 | 28 | -------------------------------------------------------------------------------- /blog/components/Empty.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 24 | -------------------------------------------------------------------------------- /blog/components/GoTop.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /blog/components/LoadingDots.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 30 | -------------------------------------------------------------------------------- /blog/components/PlayingGameList.vue: -------------------------------------------------------------------------------- 1 | 20 | 25 | 26 | -------------------------------------------------------------------------------- /blog/components/PostAbout.vue: -------------------------------------------------------------------------------- 1 | 29 | 51 | 52 | -------------------------------------------------------------------------------- /blog/components/PostListFilterBtn.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /blog/components/PostShowHeaderListBtn.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /blog/components/PostVote.vue: -------------------------------------------------------------------------------- 1 | 21 | 43 | 44 | -------------------------------------------------------------------------------- /blog/components/Rating.vue: -------------------------------------------------------------------------------- 1 | 18 | 26 | 38 | -------------------------------------------------------------------------------- /blog/components/ReadingBookList.vue: -------------------------------------------------------------------------------- 1 | 20 | 25 | 26 | -------------------------------------------------------------------------------- /blog/error.vue: -------------------------------------------------------------------------------- 1 | 9 | 15 | 45 | -------------------------------------------------------------------------------- /blog/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nuxt-app", 3 | "private": true, 4 | "type": "module", 5 | "scripts": { 6 | "build": "nuxt build", 7 | "dev": "nuxt dev", 8 | "generate": "nuxt generate", 9 | "preview": "nuxt preview", 10 | "postinstall": "nuxt prepare" 11 | }, 12 | "devDependencies": { 13 | "@nuxt/devtools": "latest", 14 | "nuxt": "^3.14.1592", 15 | "vue": "^3.3.10", 16 | "vue-router": "^4.2.5" 17 | }, 18 | "dependencies": { 19 | "@nuxt/ui": "^2.11.1", 20 | "@photo-sphere-viewer/core": "^5.12.1", 21 | "@pinia/nuxt": "^0.5.1", 22 | "highlight.js": "^11.11.1", 23 | "moment": "^2.30.1", 24 | "nuxt-swiper": "^1.2.2", 25 | "photoswipe": "^5.4.4" 26 | }, 27 | "version": "0.29.1" 28 | } 29 | -------------------------------------------------------------------------------- /blog/pages/404.vue: -------------------------------------------------------------------------------- 1 | 9 | 16 | 46 | -------------------------------------------------------------------------------- /blog/pages/index.vue: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /blog/pages/index/page/[id].vue: -------------------------------------------------------------------------------- 1 | 4 | 9 | 10 | -------------------------------------------------------------------------------- /blog/pages/index/post/[id].vue: -------------------------------------------------------------------------------- 1 | 4 | 9 | 10 | -------------------------------------------------------------------------------- /blog/pages/index/post/list.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | -------------------------------------------------------------------------------- /blog/pages/index/post/list/[[page]]/[[type]].vue: -------------------------------------------------------------------------------- 1 | 6 | 18 | 19 | -------------------------------------------------------------------------------- /blog/pages/index/post/list/archive/[year]/[month].vue: -------------------------------------------------------------------------------- 1 | 12 | 42 | -------------------------------------------------------------------------------- /blog/pages/index/post/list/archive/[year]/[month]/[page]/[[type]].vue: -------------------------------------------------------------------------------- 1 | 4 | 9 | 10 | -------------------------------------------------------------------------------- /blog/pages/index/post/list/keyword/[keyword].vue: -------------------------------------------------------------------------------- 1 | 11 | 40 | -------------------------------------------------------------------------------- /blog/pages/index/post/list/keyword/[keyword]/[page]/[[type]].vue: -------------------------------------------------------------------------------- 1 | 4 | 9 | 10 | -------------------------------------------------------------------------------- /blog/pages/index/post/list/sort/[sortid].vue: -------------------------------------------------------------------------------- 1 | 12 | 47 | -------------------------------------------------------------------------------- /blog/pages/index/post/list/sort/[sortid]/[page]/[[type]].vue: -------------------------------------------------------------------------------- 1 | 4 | 9 | 10 | -------------------------------------------------------------------------------- /blog/pages/index/post/list/tag/[tagid].vue: -------------------------------------------------------------------------------- 1 | 12 | 43 | -------------------------------------------------------------------------------- /blog/pages/index/post/list/tag/[tagid]/[page]/[[type]].vue: -------------------------------------------------------------------------------- 1 | 4 | 9 | 10 | -------------------------------------------------------------------------------- /blog/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/favicon.ico -------------------------------------------------------------------------------- /blog/public/img/avatar/0.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/0.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/1.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/1.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/10.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/10.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/100.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/100.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/101.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/101.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/102.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/102.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/103.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/103.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/104.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/104.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/105.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/105.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/106.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/106.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/107.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/107.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/108.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/108.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/109.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/109.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/11.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/11.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/110.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/110.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/111.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/111.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/112.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/112.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/113.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/113.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/114.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/114.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/115.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/115.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/116.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/116.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/117.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/117.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/118.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/118.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/119.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/119.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/12.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/12.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/120.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/120.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/121.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/121.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/122.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/122.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/123.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/123.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/124.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/124.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/125.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/125.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/126.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/126.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/127.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/127.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/128.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/128.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/129.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/129.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/13.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/13.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/130.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/130.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/131.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/131.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/132.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/132.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/133.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/133.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/134.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/134.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/135.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/135.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/136.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/136.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/137.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/137.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/138.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/138.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/139.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/139.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/14.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/14.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/140.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/140.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/141.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/141.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/142.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/142.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/143.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/143.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/144.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/144.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/145.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/145.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/146.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/146.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/147.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/147.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/148.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/148.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/149.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/149.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/15.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/15.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/150.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/150.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/151.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/151.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/152.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/152.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/153.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/153.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/154.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/154.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/155.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/155.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/156.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/156.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/157.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/157.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/158.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/158.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/159.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/159.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/16.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/16.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/160.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/160.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/161.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/161.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/162.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/162.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/163.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/163.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/164.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/164.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/165.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/165.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/166.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/166.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/167.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/167.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/168.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/168.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/169.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/169.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/17.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/17.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/170.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/170.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/171.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/171.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/172.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/172.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/173.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/173.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/174.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/174.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/175.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/175.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/176.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/176.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/18.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/18.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/19.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/19.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/2.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/2.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/20.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/20.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/21.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/21.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/22.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/22.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/23.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/23.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/24.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/24.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/25.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/25.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/26.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/26.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/27.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/27.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/28.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/28.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/29.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/29.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/3.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/3.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/30.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/30.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/31.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/31.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/32.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/32.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/33.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/33.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/34.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/34.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/35.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/35.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/36.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/36.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/37.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/37.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/38.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/38.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/39.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/39.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/4.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/4.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/40.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/40.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/41.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/41.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/42.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/42.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/43.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/43.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/44.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/44.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/45.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/45.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/46.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/46.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/47.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/47.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/48.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/48.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/49.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/49.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/5.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/5.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/50.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/50.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/51.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/51.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/52.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/52.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/53.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/53.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/54.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/54.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/55.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/55.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/56.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/56.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/57.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/57.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/58.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/58.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/59.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/59.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/6.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/6.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/60.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/60.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/61.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/61.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/62.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/62.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/63.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/63.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/64.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/64.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/65.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/65.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/66.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/66.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/67.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/67.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/68.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/68.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/69.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/69.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/7.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/7.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/70.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/70.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/71.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/71.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/72.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/72.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/73.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/73.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/74.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/74.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/75.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/75.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/76.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/76.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/77.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/77.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/78.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/78.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/79.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/79.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/8.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/8.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/80.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/80.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/81.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/81.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/82.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/82.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/83.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/83.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/84.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/84.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/85.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/85.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/86.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/86.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/87.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/87.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/88.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/88.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/89.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/89.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/9.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/9.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/90.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/90.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/91.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/91.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/92.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/92.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/93.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/93.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/94.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/94.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/95.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/95.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/96.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/96.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/97.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/97.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/98.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/98.webp -------------------------------------------------------------------------------- /blog/public/img/avatar/99.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/avatar/99.webp -------------------------------------------------------------------------------- /blog/public/img/bg_02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/bg_02.png -------------------------------------------------------------------------------- /blog/public/img/bg_02_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/bg_02_dark.png -------------------------------------------------------------------------------- /blog/public/img/menuBg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/menuBg.png -------------------------------------------------------------------------------- /blog/public/img/mypage-banner.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/mypage-banner.webp -------------------------------------------------------------------------------- /blog/public/img/nodata.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/nodata.webp -------------------------------------------------------------------------------- /blog/public/img/nopic400-565.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeg1412/wikimoeNodeJSBlog/54bf806f4afd919c3e892e04efa6e538e48d8567/blog/public/img/nopic400-565.png -------------------------------------------------------------------------------- /blog/sample.env: -------------------------------------------------------------------------------- 1 | NUXT_API_DOMAIN=http://localhost:3000 2 | SWR_ENABLED=1 3 | SWR_CACHE_MAXAGE=10 4 | SWR_CACHE_STALEMAXAGE=3600 5 | CACHE_MAX_PAGE=10 6 | CACHE_TTL=60000 -------------------------------------------------------------------------------- /blog/server/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../.nuxt/tsconfig.server.json" 3 | } 4 | -------------------------------------------------------------------------------- /blog/store/isFullscreen.ts: -------------------------------------------------------------------------------- 1 | import { ref } from 'vue' 2 | import { defineStore } from 'pinia' 3 | 4 | export const useIsFullscreenStore = defineStore('isFullscreen', () => { 5 | const isFullscreen = ref(false) 6 | function setFullscreen(status: boolean) { 7 | isFullscreen.value = status 8 | } 9 | return { isFullscreen, setFullscreen } 10 | }) 11 | -------------------------------------------------------------------------------- /blog/store/options.ts: -------------------------------------------------------------------------------- 1 | import { ref, computed } from 'vue' 2 | import { defineStore } from 'pinia' 3 | import { getOptionsApi } from '~/api/option' 4 | 5 | export const useOptionStore = defineStore('options', () => { 6 | const options = ref(null) 7 | async function getOptions() { 8 | await getOptionsApi().then((res: any) => { 9 | options.value = res.data.value.data 10 | }) 11 | } 12 | return { options, getOptions } 13 | }) 14 | -------------------------------------------------------------------------------- /blog/tailwind.config.ts: -------------------------------------------------------------------------------- 1 | import type { Config } from 'tailwindcss' 2 | import defaultTheme from 'tailwindcss/defaultTheme' 3 | 4 | export default >{ 5 | theme: { 6 | extend: { 7 | colors: { 8 | wikimoe: { 9 | '50': '#fef1f3', 10 | '100': '#fce8eb', 11 | '200': '#f9d2db', 12 | '300': '#f4aebe', 13 | '400': '#ef8fa7', 14 | '500': '#e9819d', 15 | '600': '#d85f85', 16 | '700': '#d4356a', 17 | '800': '#bf2b64', 18 | '900': '#a72a5e', 19 | '950': '#75153a', 20 | }, 21 | }, 22 | }, 23 | fontFamily: { 24 | sans: [], 25 | serif: [], 26 | mono: [], 27 | }, 28 | }, 29 | } 30 | -------------------------------------------------------------------------------- /blog/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | // https://nuxt.com/docs/guide/concepts/typescript 3 | "extends": "./.nuxt/tsconfig.json" 4 | } 5 | -------------------------------------------------------------------------------- /build-all.js: -------------------------------------------------------------------------------- 1 | const { execSync } = require('child_process'); 2 | console.log('install and build admin'); 3 | execSync('cd admin && yarn install && yarn build && cd ..', { stdio: 'inherit' }); 4 | console.log('install server'); 5 | execSync(`cd server && yarn install && cd ..`, { stdio: 'inherit' }); 6 | console.log('install and build blog'); 7 | execSync('cd blog && yarn install && yarn build && cd ..', { stdio: 'inherit' }); -------------------------------------------------------------------------------- /example.env: -------------------------------------------------------------------------------- 1 | # THIS ENV FILE EXAMPLE ONLY FOR DOCKER COMPOSE 2 | 3 | # server 4 | # Only needed during the first initialization 5 | USER_NAME=admin 6 | # Please do not modify. If you want to modify the port mapping, please modify the compose file 7 | PORT=3006 8 | DB_HOST=mongodb://wikimoe-db:27017/wikimoeBlog 9 | JSON_LIMIT=50mb 10 | URLENCODED_LIMIT=50mb 11 | IP2LOCATION_FILE_NAME= 12 | MAX_HISTORYLOGS_SIZE=1073741824 13 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const { execSync } = require('child_process'); 2 | const concurrently = require('concurrently'); 3 | const os = require('os'); 4 | 5 | const shouldBuild = process.argv.includes('--build'); 6 | const platform = os.platform(); 7 | const startCommand = platform === 'win32' ? 'start-windows' : 'start-linux'; 8 | 9 | if (shouldBuild) { 10 | execSync('cd admin && yarn install && yarn build && cd ..', { stdio: 'inherit' }); 11 | execSync(`cd server && yarn install && cd ..`, { stdio: 'inherit' }); 12 | execSync('cd blog && yarn install && yarn build && cd ..', { stdio: 'inherit' }); 13 | } 14 | 15 | const { result } = concurrently([ 16 | { command: `cd server && yarn run start`, name: 'server', prefixColor: 'blue' }, 17 | { command: `cd blog/build && yarn run ${startCommand}`, name: 'blog', prefixColor: 'green' }, 18 | ], { 19 | prefix: 'name', 20 | killOthers: ['failure', 'success'], 21 | }) 22 | 23 | result.then(() => { 24 | console.log('All applications are running...'); 25 | }).catch((err) => { 26 | console.error('Something went wrong:', err); 27 | }); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "wikimoenodejsblog", 3 | "version": "0.29.1", 4 | "description": "猛男自用博客系统,基于nodejs,mongodb", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "node index.js", 8 | "build-all": "node build-all.js", 9 | "ver-change": "node ver-change.js" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "git+https://github.com/eeg1412/wikimoeNodeJSBlog.git" 14 | }, 15 | "keywords": [ 16 | "blog" 17 | ], 18 | "author": "hiroki", 19 | "license": "MIT", 20 | "bugs": { 21 | "url": "https://github.com/eeg1412/wikimoeNodeJSBlog/issues" 22 | }, 23 | "homepage": "https://github.com/eeg1412/wikimoeNodeJSBlog#readme", 24 | "dependencies": { 25 | "concurrently": "^8.2.2" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /server/api/admin/album/deleteAlbum.js: -------------------------------------------------------------------------------- 1 | const albumUtils = require('../../../mongodb/utils/albums') 2 | const log4js = require('log4js') 3 | const adminApiLog = log4js.getLogger('adminApi') 4 | 5 | module.exports = async function (req, res, next) { 6 | const id = req.query.id 7 | if (!id) { 8 | res.status(400).json({ 9 | errors: [{ 10 | message: 'id不能为空' 11 | }] 12 | }) 13 | return 14 | } 15 | // 判断目录下是否有文件 16 | const album = await albumUtils.findOne({ _id: id }) 17 | // 根据album.count 18 | if (album.count > 0) { 19 | res.status(400).json({ 20 | errors: [{ 21 | message: '相册下存在媒体,不能删除' 22 | }] 23 | }) 24 | return 25 | } 26 | 27 | // 删除相册 28 | albumUtils.deleteOne({ _id: id }).then((data) => { 29 | if (data.deletedCount === 0) { 30 | res.status(400).json({ 31 | errors: [{ 32 | message: '删除失败' 33 | }] 34 | }) 35 | return 36 | } 37 | 38 | res.send({ 39 | data: { 40 | message: '删除成功' 41 | } 42 | }) 43 | }).catch((err) => { 44 | res.status(400).json({ 45 | errors: [{ 46 | message: '删除失败' 47 | }] 48 | }) 49 | adminApiLog.error(`album delete fail, ${logErrorToText(err)}`) 50 | }) 51 | } 52 | -------------------------------------------------------------------------------- /server/api/admin/album/getAlbumDetail.js: -------------------------------------------------------------------------------- 1 | 2 | const albumUtils = require('../../../mongodb/utils/albums') 3 | const utils = require('../../../utils/utils') 4 | const log4js = require('log4js') 5 | const adminApiLog = log4js.getLogger('adminApi') 6 | 7 | module.exports = async function (req, res, next) { 8 | const id = req.query.id 9 | if (!id) { 10 | res.status(400).json({ 11 | errors: [{ 12 | message: 'id不能为空' 13 | }] 14 | }) 15 | return 16 | } 17 | // findOne 18 | albumUtils.findOne({ _id: id }).then((data) => { 19 | if (!data) { 20 | res.status(400).json({ 21 | errors: [{ 22 | message: '相册不存在' 23 | }] 24 | }) 25 | return 26 | } 27 | res.send({ 28 | data: data 29 | }) 30 | }).catch((err) => { 31 | res.status(400).json({ 32 | errors: [{ 33 | message: '相册详情获取失败' 34 | }] 35 | }) 36 | adminApiLog.error(`album detail get fail, ${logErrorToText(err)}`) 37 | }) 38 | } 39 | -------------------------------------------------------------------------------- /server/api/admin/backup/getBackupDetail.js: -------------------------------------------------------------------------------- 1 | const backupUtils = require('../../../mongodb/utils/backups') 2 | const utils = require('../../../utils/utils') 3 | const log4js = require('log4js') 4 | const adminApiLog = log4js.getLogger('adminApi') 5 | 6 | module.exports = async function (req, res, next) { 7 | const id = req.query.id 8 | if (!id) { 9 | res.status(400).json({ 10 | errors: [{ 11 | message: 'id不能为空' 12 | }] 13 | }) 14 | return 15 | } 16 | // findOne 17 | backupUtils.findOne({ _id: id }).then((data) => { 18 | if (!data) { 19 | res.status(400).json({ 20 | errors: [{ 21 | message: '备份不存在' 22 | }] 23 | }) 24 | return 25 | } 26 | res.send({ 27 | data: data 28 | }) 29 | }).catch((err) => { 30 | res.status(400).json({ 31 | errors: [{ 32 | message: '备份详情获取失败' 33 | }] 34 | }) 35 | adminApiLog.error(`backup detail get fail, ${logErrorToText(err)}`) 36 | }) 37 | } 38 | -------------------------------------------------------------------------------- /server/api/admin/backup/getDownloadBackupToken.js: -------------------------------------------------------------------------------- 1 | const backupUtils = require('../../../mongodb/utils/backups') 2 | const utils = require('../../../utils/utils') 3 | const log4js = require('log4js') 4 | const adminApiLog = log4js.getLogger('adminApi') 5 | 6 | module.exports = async function (req, res, next) { 7 | const id = req.query.id 8 | // check token 9 | if (!id) { 10 | res.status(400).json({ 11 | errors: [{ 12 | message: 'id不能为空' 13 | }] 14 | }) 15 | return 16 | } 17 | // findOne 18 | backupUtils.findOne({ _id: id }).then((data) => { 19 | if (!data) { 20 | res.status(400).json({ 21 | errors: [{ 22 | message: '备份不存在' 23 | }] 24 | }) 25 | return 26 | } 27 | const token = utils.creatJWT({ id: id, tokenType: 'downloadBackup' }, '2m') 28 | res.send({ 29 | token: token 30 | }) 31 | }).catch((err) => { 32 | res.status(400).json({ 33 | errors: [{ 34 | message: '备份详情获取失败' 35 | }] 36 | }) 37 | adminApiLog.error(`backup detail get fail, ${logErrorToText(err)}`) 38 | }) 39 | } -------------------------------------------------------------------------------- /server/api/admin/backup/uploadBackupFileChunk.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | 3 | module.exports = async function (req, res, next) { 4 | const id = req.params.id; 5 | const chunkIndex = req.params.chunkindex; 6 | const file = req.file 7 | // 将分片文件保存到指定目录 8 | const chunkDir = `./cache/${id}` 9 | // 如果备份目录不存在,则报错 10 | if (!fs.existsSync(chunkDir)) { 11 | res.status(400).json({ 12 | errors: [{ 13 | message: '备份文件不存在' 14 | }] 15 | }) 16 | return 17 | } 18 | const chunkPath = `${chunkDir}/${chunkIndex}` 19 | fs.writeFileSync(chunkPath, file.buffer) 20 | // 保存成功 21 | res.status(200).json({ 22 | message: '上传成功' 23 | }) 24 | } -------------------------------------------------------------------------------- /server/api/admin/bangumi/getBangumiDetail.js: -------------------------------------------------------------------------------- 1 | const bangumiUtils = require('../../../mongodb/utils/bangumis') 2 | const utils = require('../../../utils/utils') 3 | const log4js = require('log4js') 4 | const adminApiLog = log4js.getLogger('adminApi') 5 | 6 | module.exports = async function (req, res, next) { 7 | const id = req.query.id 8 | if (!id) { 9 | res.status(400).json({ 10 | errors: [{ 11 | message: 'id不能为空' 12 | }] 13 | }) 14 | return 15 | } 16 | // findOne 17 | bangumiUtils.findOne({ _id: id }).then((data) => { 18 | if (!data) { 19 | res.status(400).json({ 20 | errors: [{ 21 | message: '番剧不存在' 22 | }] 23 | }) 24 | return 25 | } 26 | res.send({ 27 | data: data 28 | }) 29 | }).catch((err) => { 30 | res.status(400).json({ 31 | errors: [{ 32 | message: '番剧详情获取失败' 33 | }] 34 | }) 35 | adminApiLog.error(`bangumi detail get fail, ${logErrorToText(err)}`) 36 | }) 37 | } 38 | -------------------------------------------------------------------------------- /server/api/admin/banner/createBanner.js: -------------------------------------------------------------------------------- 1 | const bannerUtils = require('../../../mongodb/utils/banners') 2 | const utils = require('../../../utils/utils') 3 | const log4js = require('log4js') 4 | const adminApiLog = log4js.getLogger('adminApi') 5 | const cacheDataUtils = require('../../../config/cacheData') 6 | 7 | 8 | module.exports = async function (req, res, next) { 9 | 10 | const { title, taxis, img, link } = req.body 11 | // 校验格式 12 | const params = { 13 | title: title || '', 14 | taxis: taxis || 0, 15 | link: link || '', 16 | } 17 | // save 18 | bannerUtils.save(params).then((data) => { 19 | res.send({ 20 | data: data 21 | }) 22 | adminApiLog.info(`banner create success`) 23 | cacheDataUtils.getBannerList() 24 | // utils.reflushBlogCache() 25 | }).catch((err) => { 26 | res.status(400).json({ 27 | errors: [{ 28 | message: '横幅创建失败' 29 | }] 30 | }) 31 | adminApiLog.error(`banner create fail, ${logErrorToText(err)}`) 32 | }) 33 | 34 | } 35 | -------------------------------------------------------------------------------- /server/api/admin/banner/getBannerList.js: -------------------------------------------------------------------------------- 1 | const bannerUtils = require('../../../mongodb/utils/banners') 2 | const utils = require('../../../utils/utils') 3 | const log4js = require('log4js') 4 | const adminApiLog = log4js.getLogger('adminApi') 5 | 6 | module.exports = async function (req, res, next) { 7 | 8 | const sort = { 9 | // 排序 10 | taxis: 1, 11 | _id: -1 12 | } 13 | bannerUtils.findPage({}, sort).then((data) => { 14 | // 返回格式list,total 15 | res.send({ 16 | list: data.list, 17 | total: data.total 18 | }) 19 | 20 | }).catch((err) => { 21 | res.status(400).json({ 22 | errors: [{ 23 | message: '横幅列表获取失败' 24 | }] 25 | }) 26 | adminApiLog.error(`banner list get fail, ${JSON.stringify(err) 27 | }`) 28 | }) 29 | } 30 | -------------------------------------------------------------------------------- /server/api/admin/book/getBookDetail.js: -------------------------------------------------------------------------------- 1 | const bookUtils = require('../../../mongodb/utils/books') 2 | const utils = require('../../../utils/utils') 3 | const log4js = require('log4js') 4 | const adminApiLog = log4js.getLogger('adminApi') 5 | 6 | module.exports = async function (req, res, next) { 7 | const id = req.query.id 8 | if (!id) { 9 | res.status(400).json({ 10 | errors: [{ 11 | message: 'id不能为空' 12 | }] 13 | }) 14 | return 15 | } 16 | // findOne 17 | bookUtils.findOne({ _id: id }).then((data) => { 18 | if (!data) { 19 | res.status(400).json({ 20 | errors: [{ 21 | message: '书籍不存在' 22 | }] 23 | }) 24 | return 25 | } 26 | res.send({ 27 | data: data 28 | }) 29 | }).catch((err) => { 30 | res.status(400).json({ 31 | errors: [{ 32 | message: '书籍详情获取失败' 33 | }] 34 | }) 35 | adminApiLog.error(`book detail get fail, ${logErrorToText(err)}`) 36 | }) 37 | } 38 | -------------------------------------------------------------------------------- /server/api/admin/booktype/createBooktype.js: -------------------------------------------------------------------------------- 1 | const booktypeUtils = require('../../../mongodb/utils/booktypes') 2 | const utils = require('../../../utils/utils') 3 | const log4js = require('log4js') 4 | const adminApiLog = log4js.getLogger('adminApi') 5 | 6 | module.exports = async function (req, res, next) { 7 | const { name, color } = req.body 8 | // 校验格式 9 | const params = { 10 | name, color 11 | } 12 | const rule = [ 13 | { 14 | key: 'name', 15 | label: '书籍类型名称', 16 | type: null, 17 | required: true 18 | }, 19 | { 20 | key: 'color', 21 | label: '颜色', 22 | type: null, 23 | required: true 24 | } 25 | 26 | ] 27 | const errors = utils.checkForm(params, rule) 28 | if (errors.length > 0) { 29 | res.status(400).json({ errors }) 30 | return 31 | } 32 | // save 33 | booktypeUtils.save(params).then((data) => { 34 | res.send({ 35 | data: data 36 | }) 37 | adminApiLog.info(`booktype create success`) 38 | }).catch((err) => { 39 | res.status(400).json({ 40 | errors: [{ 41 | message: '书籍类型创建失败' 42 | }] 43 | }) 44 | adminApiLog.error(`booktype create fail, ${logErrorToText(err)}`) 45 | }) 46 | 47 | } 48 | -------------------------------------------------------------------------------- /server/api/admin/booktype/getBooktypeDetail.js: -------------------------------------------------------------------------------- 1 | const booktypeUtils = require('../../../mongodb/utils/booktypes') 2 | const utils = require('../../../utils/utils') 3 | const log4js = require('log4js') 4 | const adminApiLog = log4js.getLogger('adminApi') 5 | 6 | module.exports = async function (req, res, next) { 7 | const id = req.query.id 8 | if (!id) { 9 | res.status(400).json({ 10 | errors: [{ 11 | message: 'id不能为空' 12 | }] 13 | }) 14 | return 15 | } 16 | // findOne 17 | booktypeUtils.findOne({ _id: id }).then((data) => { 18 | if (!data) { 19 | res.status(400).json({ 20 | errors: [{ 21 | message: '书籍类型不存在' 22 | }] 23 | }) 24 | return 25 | } 26 | res.send({ 27 | data: data 28 | }) 29 | }).catch((err) => { 30 | res.status(400).json({ 31 | errors: [{ 32 | message: '书籍类型详情获取失败' 33 | }] 34 | }) 35 | adminApiLog.error(`booktype detail get fail, ${logErrorToText(err)}`) 36 | }) 37 | } 38 | -------------------------------------------------------------------------------- /server/api/admin/comment/getCommentDetail.js: -------------------------------------------------------------------------------- 1 | const commentUtils = require('../../../mongodb/utils/comments') 2 | const utils = require('../../../utils/utils') 3 | const log4js = require('log4js') 4 | const adminApiLog = log4js.getLogger('adminApi') 5 | 6 | module.exports = async function (req, res, next) { 7 | const id = req.query.id 8 | if (!id) { 9 | res.status(400).json({ 10 | errors: [{ 11 | message: 'id不能为空' 12 | }] 13 | }) 14 | return 15 | } 16 | // findOne 17 | commentUtils.findOne({ _id: id }).then((data) => { 18 | if (!data) { 19 | res.status(400).json({ 20 | errors: [{ 21 | message: '评论不存在' 22 | }] 23 | }) 24 | return 25 | } 26 | const parentId = data.populated('parent') 27 | const newData = data.toJSON() 28 | newData.parentId = parentId 29 | res.send({ 30 | data: newData 31 | }) 32 | }).catch((err) => { 33 | res.status(400).json({ 34 | errors: [{ 35 | message: '评论详情获取失败' 36 | }] 37 | }) 38 | adminApiLog.error(`comment detail get fail, ${logErrorToText(err)}`) 39 | }) 40 | } 41 | -------------------------------------------------------------------------------- /server/api/admin/commentLikeLog/getCommentLikeLogStats.js: -------------------------------------------------------------------------------- 1 | const commentLikeLogUtils = require('../../../mongodb/utils/commentLikeLogs') 2 | const utils = require('../../../utils/utils') 3 | const log4js = require('log4js') 4 | const adminApiLog = log4js.getLogger('adminApi') 5 | 6 | module.exports = async function (req, res, next) { 7 | const stats = await utils.getCommentLikeLogsSize() 8 | res.send({ 9 | stats 10 | }) 11 | } 12 | -------------------------------------------------------------------------------- /server/api/admin/config/getConfig.js: -------------------------------------------------------------------------------- 1 | module.exports = async function (req, res, next) { 2 | // 读取配置文件 3 | const config = global.$globalConfig; 4 | res.send({ 5 | data: config 6 | }) 7 | } -------------------------------------------------------------------------------- /server/api/admin/emailSendHistory/resendEmail.js: -------------------------------------------------------------------------------- 1 | const emailSendHistoryUtils = require('../../../mongodb/utils/emailSendHistorys') 2 | const utils = require('../../../utils/utils') 3 | const log4js = require('log4js') 4 | const adminApiLog = log4js.getLogger('adminApi') 5 | 6 | module.exports = async function (req, res, next) { 7 | const { id } = req.body 8 | // 查询id是否存在 9 | if (!id) { 10 | res.status(400).json({ 11 | errors: [{ 12 | message: '参数错误' 13 | }] 14 | }) 15 | return 16 | } 17 | 18 | const emailData = await emailSendHistoryUtils.findOne({ _id: id }) 19 | if (!emailData) { 20 | res.status(400).json({ 21 | errors: [{ 22 | message: '邮件发送记录不存在' 23 | }] 24 | }) 25 | return 26 | } 27 | 28 | const email = { 29 | to: emailData.to, 30 | subject: emailData.subject, 31 | content: emailData.content 32 | } 33 | // 发送邮件 34 | utils.sendEmail(email.to, email.content, email.subject) 35 | // 返回成功 36 | res.send({ 37 | message: '已重新发送邮件' 38 | }) 39 | } 40 | -------------------------------------------------------------------------------- /server/api/admin/event/getEventDetail.js: -------------------------------------------------------------------------------- 1 | const eventUtils = require('../../../mongodb/utils/events') 2 | const utils = require('../../../utils/utils') 3 | const log4js = require('log4js') 4 | const adminApiLog = log4js.getLogger('adminApi') 5 | 6 | module.exports = async function (req, res, next) { 7 | const id = req.query.id 8 | if (!id) { 9 | res.status(400).json({ 10 | errors: [{ 11 | message: 'id不能为空' 12 | }] 13 | }) 14 | return 15 | } 16 | // findOne 17 | eventUtils.findOne({ _id: id }).then((data) => { 18 | if (!data) { 19 | res.status(400).json({ 20 | errors: [{ 21 | message: '活动不存在' 22 | }] 23 | }) 24 | return 25 | } 26 | res.send({ 27 | data: data 28 | }) 29 | }).catch((err) => { 30 | res.status(400).json({ 31 | errors: [{ 32 | message: '活动详情获取失败' 33 | }] 34 | }) 35 | adminApiLog.error(`event detail get fail, ${logErrorToText(err)}`) 36 | }) 37 | } 38 | -------------------------------------------------------------------------------- /server/api/admin/eventtype/createEventtype.js: -------------------------------------------------------------------------------- 1 | const eventtypeUtils = require('../../../mongodb/utils/eventtypes') 2 | const utils = require('../../../utils/utils') 3 | const log4js = require('log4js') 4 | const adminApiLog = log4js.getLogger('adminApi') 5 | 6 | module.exports = async function (req, res, next) { 7 | 8 | const { name, color } = req.body 9 | // 校验格式 10 | const params = { 11 | name, color 12 | } 13 | const rule = [ 14 | { 15 | key: 'name', 16 | label: '活动类型名称', 17 | type: null, 18 | required: true 19 | }, 20 | { 21 | key: 'color', 22 | label: '颜色', 23 | type: null, 24 | required: true 25 | } 26 | 27 | ] 28 | const errors = utils.checkForm(params, rule) 29 | if (errors.length > 0) { 30 | res.status(400).json({ errors }) 31 | return 32 | } 33 | // save 34 | eventtypeUtils.save(params).then((data) => { 35 | res.send({ 36 | data: data 37 | }) 38 | adminApiLog.info(`eventtype create success`) 39 | }).catch((err) => { 40 | res.status(400).json({ 41 | errors: [{ 42 | message: '活动类型创建失败' 43 | }] 44 | }) 45 | adminApiLog.error(`eventtype create fail, ${logErrorToText(err)}`) 46 | }) 47 | 48 | } 49 | -------------------------------------------------------------------------------- /server/api/admin/eventtype/getEventtypeDetail.js: -------------------------------------------------------------------------------- 1 | const eventtypeUtils = require('../../../mongodb/utils/eventtypes') 2 | const utils = require('../../../utils/utils') 3 | const log4js = require('log4js') 4 | const adminApiLog = log4js.getLogger('adminApi') 5 | 6 | module.exports = async function (req, res, next) { 7 | const id = req.query.id 8 | if (!id) { 9 | res.status(400).json({ 10 | errors: [{ 11 | message: 'id不能为空' 12 | }] 13 | }) 14 | return 15 | } 16 | // findOne 17 | eventtypeUtils.findOne({ _id: id }).then((data) => { 18 | if (!data) { 19 | res.status(400).json({ 20 | errors: [{ 21 | message: '活动类型不存在' 22 | }] 23 | }) 24 | return 25 | } 26 | res.send({ 27 | data: data 28 | }) 29 | }).catch((err) => { 30 | res.status(400).json({ 31 | errors: [{ 32 | message: '活动类型详情获取失败' 33 | }] 34 | }) 35 | adminApiLog.error(`eventtype detail get fail, ${logErrorToText(err)}`) 36 | }) 37 | } 38 | -------------------------------------------------------------------------------- /server/api/admin/game/getGameDetail.js: -------------------------------------------------------------------------------- 1 | const gameUtils = require('../../../mongodb/utils/games') 2 | const utils = require('../../../utils/utils') 3 | const log4js = require('log4js') 4 | const adminApiLog = log4js.getLogger('adminApi') 5 | 6 | module.exports = async function (req, res, next) { 7 | const id = req.query.id 8 | if (!id) { 9 | res.status(400).json({ 10 | errors: [{ 11 | message: 'id不能为空' 12 | }] 13 | }) 14 | return 15 | } 16 | // findOne 17 | gameUtils.findOne({ _id: id }).then((data) => { 18 | if (!data) { 19 | res.status(400).json({ 20 | errors: [{ 21 | message: '游戏不存在' 22 | }] 23 | }) 24 | return 25 | } 26 | res.send({ 27 | data: data 28 | }) 29 | }).catch((err) => { 30 | res.status(400).json({ 31 | errors: [{ 32 | message: '游戏详情获取失败' 33 | }] 34 | }) 35 | adminApiLog.error(`game detail get fail, ${logErrorToText(err)}`) 36 | }) 37 | } 38 | -------------------------------------------------------------------------------- /server/api/admin/gamePlatform/createGamePlatform.js: -------------------------------------------------------------------------------- 1 | const gamePlatformUtils = require('../../../mongodb/utils/gamePlatforms') 2 | const utils = require('../../../utils/utils') 3 | const log4js = require('log4js') 4 | const adminApiLog = log4js.getLogger('adminApi') 5 | 6 | module.exports = async function (req, res, next) { 7 | 8 | const { name, color } = req.body 9 | // 校验格式 10 | const params = { 11 | name, color 12 | } 13 | const rule = [ 14 | { 15 | key: 'name', 16 | label: '游戏平台名称', 17 | type: null, 18 | required: true 19 | }, 20 | { 21 | key: 'color', 22 | label: '颜色', 23 | type: null, 24 | required: true 25 | } 26 | 27 | ] 28 | const errors = utils.checkForm(params, rule) 29 | if (errors.length > 0) { 30 | res.status(400).json({ errors }) 31 | return 32 | } 33 | // save 34 | gamePlatformUtils.save(params).then((data) => { 35 | res.send({ 36 | data: data 37 | }) 38 | adminApiLog.info(`gamePlatform create success`) 39 | }).catch((err) => { 40 | res.status(400).json({ 41 | errors: [{ 42 | message: '游戏平台创建失败' 43 | }] 44 | }) 45 | adminApiLog.error(`gamePlatform create fail, ${logErrorToText(err)}`) 46 | }) 47 | 48 | } 49 | -------------------------------------------------------------------------------- /server/api/admin/gamePlatform/getGamePlatformDetail.js: -------------------------------------------------------------------------------- 1 | const gamePlatformUtils = require('../../../mongodb/utils/gamePlatforms') 2 | const utils = require('../../../utils/utils') 3 | const log4js = require('log4js') 4 | const adminApiLog = log4js.getLogger('adminApi') 5 | 6 | module.exports = async function (req, res, next) { 7 | const id = req.query.id 8 | if (!id) { 9 | res.status(400).json({ 10 | errors: [{ 11 | message: 'id不能为空' 12 | }] 13 | }) 14 | return 15 | } 16 | // findOne 17 | gamePlatformUtils.findOne({ _id: id }).then((data) => { 18 | if (!data) { 19 | res.status(400).json({ 20 | errors: [{ 21 | message: '游戏平台不存在' 22 | }] 23 | }) 24 | return 25 | } 26 | res.send({ 27 | data: data 28 | }) 29 | }).catch((err) => { 30 | res.status(400).json({ 31 | errors: [{ 32 | message: '游戏平台详情获取失败' 33 | }] 34 | }) 35 | adminApiLog.error(`gamePlatform detail get fail, ${logErrorToText(err)}`) 36 | }) 37 | } 38 | -------------------------------------------------------------------------------- /server/api/admin/link/getlinkDetail.js: -------------------------------------------------------------------------------- 1 | const linkUtils = require('../../../mongodb/utils/links') 2 | const utils = require('../../../utils/utils') 3 | const log4js = require('log4js') 4 | const adminApiLog = log4js.getLogger('adminApi') 5 | 6 | module.exports = async function (req, res, next) { 7 | const id = req.query.id 8 | if (!id) { 9 | res.status(400).json({ 10 | errors: [{ 11 | message: 'id不能为空' 12 | }] 13 | }) 14 | return 15 | } 16 | // findOne 17 | linkUtils.findOne({ _id: id }).then((data) => { 18 | if (!data) { 19 | res.status(400).json({ 20 | errors: [{ 21 | message: '友链不存在' 22 | }] 23 | }) 24 | return 25 | } 26 | res.send({ 27 | data: data 28 | }) 29 | }).catch((err) => { 30 | res.status(400).json({ 31 | errors: [{ 32 | message: '友链详情获取失败' 33 | }] 34 | }) 35 | adminApiLog.error(`link detail get fail, ${logErrorToText(err)}`) 36 | }) 37 | } 38 | -------------------------------------------------------------------------------- /server/api/admin/movie/getMovieDetail.js: -------------------------------------------------------------------------------- 1 | const movieUtils = require('../../../mongodb/utils/movies') 2 | const utils = require('../../../utils/utils') 3 | const log4js = require('log4js') 4 | const adminApiLog = log4js.getLogger('adminApi') 5 | 6 | module.exports = async function (req, res, next) { 7 | const id = req.query.id 8 | if (!id) { 9 | res.status(400).json({ 10 | errors: [{ 11 | message: 'id不能为空' 12 | }] 13 | }) 14 | return 15 | } 16 | // findOne 17 | movieUtils.findOne({ _id: id }).then((data) => { 18 | if (!data) { 19 | res.status(400).json({ 20 | errors: [{ 21 | message: '电影不存在' 22 | }] 23 | }) 24 | return 25 | } 26 | res.send({ 27 | data: data 28 | }) 29 | }).catch((err) => { 30 | res.status(400).json({ 31 | errors: [{ 32 | message: '电影详情获取失败' 33 | }] 34 | }) 35 | adminApiLog.error(`movie detail get fail, ${logErrorToText(err)}`) 36 | }) 37 | } 38 | -------------------------------------------------------------------------------- /server/api/admin/navi/deletenavi.js: -------------------------------------------------------------------------------- 1 | const naviUtils = require('../../../mongodb/utils/navis') 2 | const utils = require('../../../utils/utils') 3 | const log4js = require('log4js') 4 | const adminApiLog = log4js.getLogger('adminApi') 5 | const cacheDataUtils = require('../../../config/cacheData') 6 | 7 | module.exports = async function (req, res, next) { 8 | const id = req.query.id 9 | if (!id) { 10 | res.status(400).json({ 11 | errors: [{ 12 | message: 'id不能为空' 13 | }] 14 | }) 15 | return 16 | } 17 | // 删除导航 18 | naviUtils.deleteOne({ _id: id }).then((data) => { 19 | if (data.deletedCount === 0) { 20 | res.status(400).json({ 21 | errors: [{ 22 | message: '删除失败' 23 | }] 24 | }) 25 | return 26 | } 27 | res.send({ 28 | data: { 29 | message: '删除成功' 30 | } 31 | }) 32 | cacheDataUtils.getNaviList() 33 | // utils.reflushBlogCache() 34 | }).catch((err) => { 35 | res.status(400).json({ 36 | errors: [{ 37 | message: '删除失败' 38 | }] 39 | }) 40 | adminApiLog.error(`navi delete fail, ${logErrorToText(err)}`) 41 | }) 42 | } -------------------------------------------------------------------------------- /server/api/admin/navi/getnaviDetail.js: -------------------------------------------------------------------------------- 1 | const naviUtils = require('../../../mongodb/utils/navis') 2 | const utils = require('../../../utils/utils') 3 | const log4js = require('log4js') 4 | const adminApiLog = log4js.getLogger('adminApi') 5 | 6 | module.exports = async function (req, res, next) { 7 | const id = req.query.id 8 | if (!id) { 9 | res.status(400).json({ 10 | errors: [{ 11 | message: 'id不能为空' 12 | }] 13 | }) 14 | return 15 | } 16 | // findOne 17 | naviUtils.findOne({ _id: id }).then((data) => { 18 | if (!data) { 19 | res.status(400).json({ 20 | errors: [{ 21 | message: '导航不存在' 22 | }] 23 | }) 24 | return 25 | } 26 | res.send({ 27 | data: data 28 | }) 29 | }).catch((err) => { 30 | res.status(400).json({ 31 | errors: [{ 32 | message: '导航详情获取失败' 33 | }] 34 | }) 35 | adminApiLog.error(`navi detail get fail, ${logErrorToText(err)}`) 36 | }) 37 | } 38 | -------------------------------------------------------------------------------- /server/api/admin/navi/getnaviList.js: -------------------------------------------------------------------------------- 1 | const naviUtils = require('../../../mongodb/utils/navis') 2 | const utils = require('../../../utils/utils') 3 | const log4js = require('log4js') 4 | const adminApiLog = log4js.getLogger('adminApi') 5 | 6 | module.exports = async function (req, res, next) { 7 | const sort = { 8 | taxis: 1, 9 | _id: -1 10 | } 11 | naviUtils.find({}, sort).then((data) => { 12 | // 根据返回的data,配合parent字段,生成树形结构 13 | const jsonData = data.map(doc => doc.toJSON()) 14 | const treeData = utils.generateTreeData(jsonData) 15 | res.send({ 16 | data: treeData 17 | }) 18 | }).catch((err) => { 19 | res.status(400).json({ 20 | errors: [{ 21 | message: '导航列表获取失败' 22 | }] 23 | }) 24 | adminApiLog.error(`navi list get fail, ${JSON.stringify(err) 25 | }`) 26 | }) 27 | } 28 | -------------------------------------------------------------------------------- /server/api/admin/option/getoptionList.js: -------------------------------------------------------------------------------- 1 | const optionUtils = require('../../../mongodb/utils/options') 2 | const utils = require('../../../utils/utils') 3 | const log4js = require('log4js') 4 | const adminApiLog = log4js.getLogger('adminApi') 5 | 6 | module.exports = async function (req, res, next) { 7 | let nameList = req.query.nameList || [] 8 | 9 | const params = { 10 | 11 | } 12 | if (nameList.length > 0) { 13 | params.name = { $in: nameList } 14 | // 注意博客端获取时要注意邮箱密码等敏感信息的过滤 15 | } 16 | 17 | optionUtils.find(params).then((data) => { 18 | // 返回格式list,total 19 | res.send({ 20 | data 21 | }) 22 | 23 | }).catch((err) => { 24 | res.status(400).json({ 25 | errors: [{ 26 | message: '配置项列表获取失败' 27 | }] 28 | }) 29 | adminApiLog.error(`option list get fail, ${JSON.stringify(err) 30 | }`) 31 | }) 32 | } 33 | -------------------------------------------------------------------------------- /server/api/admin/option/reflushJWTScretAdmin.js: -------------------------------------------------------------------------------- 1 | const utils = require('../../../utils/utils') 2 | const log4js = require('log4js') 3 | const adminApiLog = log4js.getLogger('adminApi') 4 | 5 | module.exports = async function (req, res, next) { 6 | try { 7 | const JWTSecretAdmin = utils.ensureJWTSecretAdmin(true) 8 | global.$secret.JWTSecretAdmin = JWTSecretAdmin 9 | res.send({ 10 | message: '刷新成功' 11 | }) 12 | adminApiLog.info(`JWTSecretAdmin reflush success`) 13 | } catch (error) { 14 | adminApiLog.error(`JWTSecretAdmin reflush failed: ${logErrorToText(error)}`) 15 | res.status(400).json({ 16 | errors: [{ 17 | message: '刷新失败' 18 | }] 19 | }) 20 | } 21 | } -------------------------------------------------------------------------------- /server/api/admin/option/reflushJWTScretBlog.js: -------------------------------------------------------------------------------- 1 | const utils = require('../../../utils/utils') 2 | const log4js = require('log4js') 3 | const adminApiLog = log4js.getLogger('adminApi') 4 | 5 | module.exports = async function (req, res, next) { 6 | try { 7 | const JWTSecretBlog = utils.ensureJWTSecretBlog(true) 8 | global.$secret.JWTSecretBlog = JWTSecretBlog 9 | res.send({ 10 | message: '刷新成功' 11 | }) 12 | adminApiLog.info(`JWTSecretBlog reflush success`) 13 | } catch (error) { 14 | adminApiLog.error(`JWTSecretBlog reflush failed: ${logErrorToText(error)}`) 15 | res.status(400).json({ 16 | errors: [{ 17 | message: '刷新失败' 18 | }] 19 | }) 20 | } 21 | } -------------------------------------------------------------------------------- /server/api/admin/post/createPost.js: -------------------------------------------------------------------------------- 1 | const postUtils = require('../../../mongodb/utils/posts') 2 | const utils = require('../../../utils/utils') 3 | const log4js = require('log4js') 4 | const adminApiLog = log4js.getLogger('adminApi') 5 | const cacheDataUtils = require('../../../config/cacheData') 6 | 7 | 8 | module.exports = async function (req, res, next) { 9 | const type = req.body.type 10 | // 校验type只能为1,2,3 11 | if (type !== 1 && type !== 2 && type !== 3) { 12 | res.status(400).json({ 13 | errors: [{ 14 | message: 'type格式错误' 15 | }] 16 | }) 17 | return 18 | } 19 | // 获取管理员ID 20 | const adminId = req.admin._id 21 | // 校验格式 22 | const params = { 23 | type: type, 24 | author: adminId, 25 | lastChangDate: new Date() 26 | } 27 | 28 | // save 29 | postUtils.save(params).then((data) => { 30 | res.send({ 31 | data: data 32 | }) 33 | adminApiLog.info(`post create success`) 34 | cacheDataUtils.getPostArchiveList() 35 | // utils.reflushBlogCache() 36 | }).catch((err) => { 37 | res.status(400).json({ 38 | errors: [{ 39 | message: '创建文章失败' 40 | }] 41 | }) 42 | adminApiLog.error(`post create fail, ${logErrorToText(err)}`) 43 | }) 44 | 45 | } 46 | -------------------------------------------------------------------------------- /server/api/admin/post/getPostDetail.js: -------------------------------------------------------------------------------- 1 | 2 | const postUtils = require('../../../mongodb/utils/posts') 3 | const utils = require('../../../utils/utils') 4 | const log4js = require('log4js') 5 | const adminApiLog = log4js.getLogger('adminApi') 6 | 7 | module.exports = async function (req, res, next) { 8 | const id = req.query.id 9 | if (!id) { 10 | res.status(400).json({ 11 | errors: [{ 12 | message: 'id不能为空' 13 | }] 14 | }) 15 | return 16 | } 17 | // findOne 18 | postUtils.findOne({ _id: id }, undefined, { isAdmin: true }).then((data) => { 19 | if (!data) { 20 | res.status(404).json({ 21 | errors: [{ 22 | message: '文章不存在' 23 | }] 24 | }) 25 | return 26 | } 27 | res.send({ 28 | data: data 29 | }) 30 | }).catch((err) => { 31 | res.status(400).json({ 32 | errors: [{ 33 | message: '文章详情获取失败' 34 | }] 35 | }) 36 | adminApiLog.error(`post detail get fail, ${logErrorToText(err)}`) 37 | }) 38 | } 39 | -------------------------------------------------------------------------------- /server/api/admin/postLikeLog/getPostLikeLogStats.js: -------------------------------------------------------------------------------- 1 | const postLikeLogUtils = require('../../../mongodb/utils/postLikeLogs') 2 | const utils = require('../../../utils/utils') 3 | const log4js = require('log4js') 4 | const adminApiLog = log4js.getLogger('adminApi') 5 | 6 | module.exports = async function (req, res, next) { 7 | const stats = await utils.getPostLikeLogsSize() 8 | res.send({ 9 | stats 10 | }) 11 | } 12 | -------------------------------------------------------------------------------- /server/api/admin/readerlog/getReaderlogStats.js: -------------------------------------------------------------------------------- 1 | const readerlogUtils = require('../../../mongodb/utils/readerlogs') 2 | const utils = require('../../../utils/utils') 3 | const log4js = require('log4js') 4 | const adminApiLog = log4js.getLogger('adminApi') 5 | 6 | module.exports = async function (req, res, next) { 7 | const stats = await utils.getReaderlogsSize() 8 | res.send({ 9 | stats 10 | }) 11 | } 12 | -------------------------------------------------------------------------------- /server/api/admin/sidebar/deleteSidebar.js: -------------------------------------------------------------------------------- 1 | const sidebarUtils = require('../../../mongodb/utils/sidebars') 2 | const utils = require('../../../utils/utils') 3 | const log4js = require('log4js') 4 | const adminApiLog = log4js.getLogger('adminApi') 5 | const cacheDataUtils = require('../../../config/cacheData') 6 | 7 | module.exports = async function (req, res, next) { 8 | const id = req.query.id 9 | if (!id) { 10 | res.status(400).json({ 11 | errors: [{ 12 | message: 'id不能为空' 13 | }] 14 | }) 15 | return 16 | } 17 | // 删除侧边栏 18 | sidebarUtils.deleteOne({ _id: id }).then((data) => { 19 | if (data.deletedCount === 0) { 20 | res.status(400).json({ 21 | errors: [{ 22 | message: '删除失败' 23 | }] 24 | }) 25 | return 26 | } 27 | res.send({ 28 | data: { 29 | message: '删除成功' 30 | } 31 | }) 32 | adminApiLog.info(`sidebar delete success`) 33 | cacheDataUtils.getSidebarList() 34 | // utils.reflushBlogCache() 35 | }).catch((err) => { 36 | res.status(400).json({ 37 | errors: [{ 38 | message: '删除失败' 39 | }] 40 | }) 41 | adminApiLog.error(`sidebar delete fail, ${logErrorToText(err)}`) 42 | }) 43 | } -------------------------------------------------------------------------------- /server/api/admin/sidebar/getSidebarDetail.js: -------------------------------------------------------------------------------- 1 | const sidebarUtils = require('../../../mongodb/utils/sidebars') 2 | const utils = require('../../../utils/utils') 3 | const log4js = require('log4js') 4 | const adminApiLog = log4js.getLogger('adminApi') 5 | 6 | module.exports = async function (req, res, next) { 7 | const id = req.query.id 8 | if (!id) { 9 | res.status(400).json({ 10 | errors: [{ 11 | message: 'id不能为空' 12 | }] 13 | }) 14 | return 15 | } 16 | // findOne 17 | sidebarUtils.findOne({ _id: id }).then((data) => { 18 | if (!data) { 19 | res.status(400).json({ 20 | errors: [{ 21 | message: '侧边栏不存在' 22 | }] 23 | }) 24 | return 25 | } 26 | res.send({ 27 | data: data 28 | }) 29 | }).catch((err) => { 30 | res.status(400).json({ 31 | errors: [{ 32 | message: '侧边栏详情获取失败' 33 | }] 34 | }) 35 | adminApiLog.error(`sidebar detail get fail, ${logErrorToText(err)}`) 36 | }) 37 | } 38 | -------------------------------------------------------------------------------- /server/api/admin/sidebar/getSidebarList.js: -------------------------------------------------------------------------------- 1 | const sidebarUtils = require('../../../mongodb/utils/sidebars') 2 | const utils = require('../../../utils/utils') 3 | const log4js = require('log4js') 4 | const adminApiLog = log4js.getLogger('adminApi') 5 | 6 | module.exports = async function (req, res, next) { 7 | const sort = { 8 | // 排序 9 | taxis: 1, 10 | // id 11 | _id: -1 12 | } 13 | sidebarUtils.find({}, sort).then((data) => { 14 | // 返回格式list,total 15 | res.send({ 16 | list: data, 17 | }) 18 | 19 | }).catch((err) => { 20 | res.status(400).json({ 21 | errors: [{ 22 | message: '侧边栏列表获取失败' 23 | }] 24 | }) 25 | adminApiLog.error(`sidebar list get fail, ${JSON.stringify(err) 26 | }`) 27 | }) 28 | } 29 | -------------------------------------------------------------------------------- /server/api/admin/sort/getSortDetail.js: -------------------------------------------------------------------------------- 1 | 2 | const sortUtils = require('../../../mongodb/utils/sorts') 3 | const utils = require('../../../utils/utils') 4 | const log4js = require('log4js') 5 | const adminApiLog = log4js.getLogger('adminApi') 6 | 7 | module.exports = async function (req, res, next) { 8 | const id = req.query.id 9 | if (!id) { 10 | res.status(400).json({ 11 | errors: [{ 12 | message: 'id不能为空' 13 | }] 14 | }) 15 | return 16 | } 17 | // findOne 18 | sortUtils.findOne({ _id: id }).then((data) => { 19 | if (!data) { 20 | res.status(400).json({ 21 | errors: [{ 22 | message: '分类不存在' 23 | }] 24 | }) 25 | return 26 | } 27 | res.send({ 28 | data: data 29 | }) 30 | }).catch((err) => { 31 | res.status(400).json({ 32 | errors: [{ 33 | message: '分类详情获取失败' 34 | }] 35 | }) 36 | adminApiLog.error(`sort detail get fail, ${logErrorToText(err)}`) 37 | }) 38 | } 39 | -------------------------------------------------------------------------------- /server/api/admin/tag/createTag.js: -------------------------------------------------------------------------------- 1 | const tagUtils = require('../../../mongodb/utils/tags') 2 | const utils = require('../../../utils/utils') 3 | const log4js = require('log4js') 4 | const adminApiLog = log4js.getLogger('adminApi') 5 | 6 | module.exports = async function (req, res, next) { 7 | 8 | const { tagname } = req.body 9 | // 校验格式 10 | const params = { 11 | tagname: utils.replaceSpacesWithUnderscores(tagname || ''), 12 | } 13 | const rule = [ 14 | { 15 | key: 'tagname', 16 | label: '标签名称', 17 | type: null, 18 | required: true, 19 | }, 20 | ] 21 | const errors = utils.checkForm(params, rule) 22 | if (errors.length > 0) { 23 | res.status(400).json({ errors }) 24 | return 25 | } 26 | // save 27 | tagUtils.save(params).then((data) => { 28 | res.send({ 29 | data: data 30 | }) 31 | // utils.reflushBlogCache() 32 | adminApiLog.info(`tag:${tagname} create success`) 33 | }).catch((err) => { 34 | res.status(400).json({ 35 | errors: [{ 36 | message: '标签创建失败' 37 | }] 38 | }) 39 | adminApiLog.error(`tag:${tagname} create fail, ${logErrorToText(err)}`) 40 | }) 41 | 42 | } 43 | -------------------------------------------------------------------------------- /server/api/admin/tag/getTagDetail.js: -------------------------------------------------------------------------------- 1 | const tagUtils = require('../../../mongodb/utils/tags') 2 | const utils = require('../../../utils/utils') 3 | const log4js = require('log4js') 4 | const adminApiLog = log4js.getLogger('adminApi') 5 | 6 | module.exports = async function (req, res, next) { 7 | const id = req.query.id 8 | if (!id) { 9 | res.status(400).json({ 10 | errors: [{ 11 | message: 'id不能为空' 12 | }] 13 | }) 14 | return 15 | } 16 | // findOne 17 | tagUtils.findOne({ _id: id }).then((data) => { 18 | if (!data) { 19 | res.status(400).json({ 20 | errors: [{ 21 | message: '标签不存在' 22 | }] 23 | }) 24 | return 25 | } 26 | res.send({ 27 | data: data 28 | }) 29 | }).catch((err) => { 30 | res.status(400).json({ 31 | errors: [{ 32 | message: '标签详情获取失败' 33 | }] 34 | }) 35 | adminApiLog.error(`tag detail get fail, ${logErrorToText(err)}`) 36 | }) 37 | } 38 | -------------------------------------------------------------------------------- /server/api/admin/tag/updateTagLastusetime.js: -------------------------------------------------------------------------------- 1 | const tagUtils = require('../../../mongodb/utils/tags') 2 | const utils = require('../../../utils/utils') 3 | const log4js = require('log4js') 4 | const adminApiLog = log4js.getLogger('adminApi') 5 | 6 | module.exports = async function (req, res, next) { 7 | // updateTagLastusetime 8 | const id = req.body.id 9 | const lastusetime = new Date() 10 | const params = { 11 | lastusetime: lastusetime, 12 | } 13 | // updateOne 14 | tagUtils.updateOne({ _id: id }, params).then((data) => { 15 | if (data.modifiedCount === 0) { 16 | res.status(400).json({ 17 | errors: [{ 18 | message: '更新失败' 19 | }] 20 | }) 21 | return 22 | } 23 | res.send({ 24 | data: data 25 | }) 26 | adminApiLog.info(`tag update usetime success`) 27 | }).catch((err) => { 28 | res.status(400).json({ 29 | errors: [{ 30 | message: '标签更新失败' 31 | }] 32 | }) 33 | adminApiLog.error(`tag update usetime fail, ${logErrorToText(err)}`) 34 | }) 35 | } 36 | -------------------------------------------------------------------------------- /server/api/admin/user/getLoginUserInfo.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = async function (req, res, next) { 3 | const adminData = { 4 | nickname: req.admin.nickname, 5 | id: req.admin._id, 6 | role: req.admin.role, 7 | photo: req.admin.photo || null, 8 | email: req.admin.email || null, 9 | description: req.admin.description || null, 10 | cover: req.admin.cover || null 11 | } 12 | res.send({ 13 | data: adminData 14 | }) 15 | } 16 | -------------------------------------------------------------------------------- /server/api/admin/user/getUserDetail.js: -------------------------------------------------------------------------------- 1 | const userUtils = require('../../../mongodb/utils/users') 2 | const utils = require('../../../utils/utils') 3 | const log4js = require('log4js') 4 | const adminApiLog = log4js.getLogger('adminApi') 5 | 6 | module.exports = async function (req, res, next) { 7 | const id = req.query.id 8 | if (!id) { 9 | res.status(400).json({ 10 | errors: [{ 11 | message: 'id不能为空' 12 | }] 13 | }) 14 | return 15 | } 16 | // findOne 17 | userUtils.findOne({ _id: id }, '-password').then((data) => { 18 | if (!data) { 19 | res.status(400).json({ 20 | errors: [{ 21 | message: '管理员不存在' 22 | }] 23 | }) 24 | return 25 | } 26 | res.send({ 27 | data: data 28 | }) 29 | }).catch((err) => { 30 | res.status(400).json({ 31 | errors: [{ 32 | message: '管理员详情获取失败' 33 | }] 34 | }) 35 | adminApiLog.error(`admin detail get fail, ${logErrorToText(err)}`) 36 | }) 37 | } 38 | -------------------------------------------------------------------------------- /server/api/admin/vote/getVoteDetail.js: -------------------------------------------------------------------------------- 1 | const voteUtils = require('../../../mongodb/utils/votes') 2 | const utils = require('../../../utils/utils') 3 | const log4js = require('log4js') 4 | const adminApiLog = log4js.getLogger('adminApi') 5 | 6 | module.exports = async function (req, res, next) { 7 | const id = req.query.id 8 | if (!id) { 9 | res.status(400).json({ 10 | errors: [{ 11 | message: 'id不能为空' 12 | }] 13 | }) 14 | return 15 | } 16 | // findOne 17 | voteUtils.findOne({ _id: id }).then((data) => { 18 | if (!data) { 19 | res.status(400).json({ 20 | errors: [{ 21 | message: '投票不存在' 22 | }] 23 | }) 24 | return 25 | } 26 | res.send({ 27 | data: data 28 | }) 29 | }).catch((err) => { 30 | res.status(400).json({ 31 | errors: [{ 32 | message: '投票详情获取失败' 33 | }] 34 | }) 35 | adminApiLog.error(`vote detail get fail, ${logErrorToText(err)}`) 36 | }) 37 | } 38 | -------------------------------------------------------------------------------- /server/api/admin/votelog/getVotelogStats.js: -------------------------------------------------------------------------------- 1 | const utils = require('../../../utils/utils') 2 | const log4js = require('log4js') 3 | 4 | module.exports = async function (req, res, next) { 5 | const stats = await utils.getVoteLogsSize() 6 | res.send({ 7 | stats 8 | }) 9 | } 10 | -------------------------------------------------------------------------------- /server/api/blog/attachment/getAttachmentList.js: -------------------------------------------------------------------------------- 1 | 2 | const attachmentUtils = require('../../../mongodb/utils/attachments') 3 | const utils = require('../../../utils/utils') 4 | const log4js = require('log4js') 5 | const userApiLog = log4js.getLogger('userApi') 6 | 7 | module.exports = async function (req, res, next) { 8 | let { album } = req.query 9 | const params = { 10 | } 11 | if (album && utils.isObjectId(album)) { 12 | params.album = album 13 | } else { 14 | // 报错 15 | res.status(400).json({ 16 | errors: [{ 17 | message: '参数错误' 18 | }] 19 | }) 20 | return 21 | } 22 | 23 | // updatedAt越新越靠前,_id越新越靠前 24 | const sort = { 25 | updatedAt: -1, 26 | _id: -1 27 | } 28 | attachmentUtils.find(params, sort, 'album description filepath filename height mimetype name status thumHeight thumWidth thumfor width _id').then((data) => { 29 | // 返回格式list,total 30 | res.send({ 31 | data: data, 32 | }) 33 | 34 | }).catch((err) => { 35 | res.status(400).json({ 36 | errors: [{ 37 | message: '媒体列表获取失败' 38 | }] 39 | }) 40 | userApiLog.error(`attachment list get fail, ${logErrorToText(err)}`) 41 | }) 42 | } 43 | -------------------------------------------------------------------------------- /server/api/blog/bangumi/getBangumiSeasonList.js: -------------------------------------------------------------------------------- 1 | const cacheDataUtils = require('../../../config/cacheData') 2 | const log4js = require('log4js') 3 | const userApiLog = log4js.getLogger('userApi') 4 | 5 | module.exports = async function (req, res, next) { 6 | if (global.$cacheData?.bangumiSeasonObj) { 7 | cacheDataUtils.checkBangumiSeasonList().then((data) => { 8 | res.send(data.list) 9 | }).catch((err) => { 10 | res.status(400).json({ 11 | errors: [{ 12 | message: '当季追番列表获取失败' 13 | }] 14 | }) 15 | userApiLog.error(`checkBangumiSeasonList get fail, ${JSON.stringify(err)}`) 16 | }) 17 | } else { 18 | cacheDataUtils.getBangumiSeasonList().then((data) => { 19 | res.send(data.list) 20 | }).catch((err) => { 21 | res.status(400).json({ 22 | errors: [{ 23 | message: '当季追番列表获取失败' 24 | }] 25 | }) 26 | userApiLog.error(`getBangumiSeasonList get fail, ${JSON.stringify(err)}`) 27 | }) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /server/api/blog/bangumi/getBangumiYearList.js: -------------------------------------------------------------------------------- 1 | const cacheDataUtils = require('../../../config/cacheData') 2 | const log4js = require('log4js') 3 | const userApiLog = log4js.getLogger('userApi') 4 | 5 | module.exports = async function (req, res, next) { 6 | if (global.$cacheData?.bangumiYearList) { 7 | res.send({ 8 | data: global.$cacheData.bangumiYearList 9 | }) 10 | } else { 11 | cacheDataUtils.getBangumiYearList().then((data) => { 12 | res.send({ 13 | data 14 | }) 15 | }).catch((err) => { 16 | res.status(400).json({ 17 | errors: [{ 18 | message: '番剧年份列表获取失败' 19 | }] 20 | }) 21 | userApiLog.error(`bangumi list get fail, ${JSON.stringify(err) 22 | }`) 23 | }) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /server/api/blog/banner/getBannerList.js: -------------------------------------------------------------------------------- 1 | const cacheDataUtils = require('../../../config/cacheData') 2 | const log4js = require('log4js') 3 | const userApiLog = log4js.getLogger('userApi') 4 | 5 | module.exports = async function (req, res, next) { 6 | if (global.$cacheData?.bannerList) { 7 | res.send(global.$cacheData.bannerList) 8 | } else { 9 | cacheDataUtils.getBannerList().then((data) => { 10 | res.send(data) 11 | }).catch((err) => { 12 | res.status(400).json({ 13 | errors: [{ 14 | message: 'banner列表获取失败' 15 | }] 16 | }) 17 | userApiLog.error(`banner list get fail, ${JSON.stringify(err) 18 | }`) 19 | }) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /server/api/blog/book/getBooktypeList.js: -------------------------------------------------------------------------------- 1 | const booktypeUtils = require('../../../mongodb/utils/booktypes') 2 | const utils = require('../../../utils/utils') 3 | const log4js = require('log4js') 4 | const userApiLog = log4js.getLogger('userApi') 5 | 6 | module.exports = async function (req, res, next) { 7 | const params = { 8 | } 9 | 10 | 11 | const sort = { 12 | _id: -1 13 | } 14 | booktypeUtils.find(params, sort, '_id color name').then((data) => { 15 | // 返回格式list,total 16 | res.send({ 17 | data: data, 18 | }) 19 | 20 | }).catch((err) => { 21 | res.status(400).json({ 22 | errors: [{ 23 | message: '书籍类型列表获取失败' 24 | }] 25 | }) 26 | userApiLog.error(`gamePlatform list get fail, ${JSON.stringify(err) 27 | }`) 28 | }) 29 | } 30 | -------------------------------------------------------------------------------- /server/api/blog/book/getReadingBookList.js: -------------------------------------------------------------------------------- 1 | const cacheDataUtils = require('../../../config/cacheData') 2 | const log4js = require('log4js') 3 | const userApiLog = log4js.getLogger('userApi') 4 | 5 | module.exports = async function (req, res, next) { 6 | if (global.$cacheData?.getReadingBookList) { 7 | res.send(global.$cacheData.getReadingBookList) 8 | } else { 9 | cacheDataUtils.getReadingBookList().then((data) => { 10 | res.send(data) 11 | }).catch((err) => { 12 | res.status(400).json({ 13 | errors: [{ 14 | message: 'getReadingBookList 列表获取失败' 15 | }] 16 | }) 17 | userApiLog.error(`getReadingBookList fail, ${JSON.stringify(err)}`) 18 | }) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /server/api/blog/comment/getLatestComments.js: -------------------------------------------------------------------------------- 1 | const cacheDataUtils = require('../../../config/cacheData') 2 | const log4js = require('log4js') 3 | const userApiLog = log4js.getLogger('userApi') 4 | 5 | module.exports = async function (req, res, next) { 6 | if (global.$cacheData?.commentList) { 7 | res.send(global.$cacheData.commentList) 8 | } else { 9 | cacheDataUtils.getCommentList().then((data) => { 10 | res.send(data) 11 | }).catch((err) => { 12 | res.status(400).json({ 13 | errors: [{ 14 | message: 'comment列表获取失败' 15 | }] 16 | }) 17 | userApiLog.error(`comment list get fail, ${JSON.stringify(err) 18 | }`) 19 | }) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /server/api/blog/commentLikeLog/getPostCommentLogList.js: -------------------------------------------------------------------------------- 1 | const commentLikeLogUtils = require('../../../mongodb/utils/commentLikeLogs') 2 | const utils = require('../../../utils/utils') 3 | const log4js = require('log4js') 4 | const userApiLog = log4js.getLogger('userApi') 5 | 6 | module.exports = async function (req, res, next) { 7 | // 获取id列表 8 | const commentIdList = req.body.commentIdList 9 | const uuid = req.headers['wmb-request-id'] 10 | const ip = utils.getUserIp(req) 11 | // 判断uuid是否符合格式 12 | if (!utils.isUUID(uuid)) { 13 | res.status(400).json({ 14 | errors: [{ 15 | message: '参数错误' 16 | }] 17 | }) 18 | return 19 | } 20 | 21 | const params = { 22 | comment: { 23 | $in: commentIdList 24 | }, 25 | // 判断uuid 26 | uuid, 27 | } 28 | 29 | const sort = { 30 | _id: -1 31 | } 32 | commentLikeLogUtils.find(params, sort, '_id comment like __v').then((data) => { 33 | res.send({ 34 | list: data 35 | }) 36 | }).catch((err) => { 37 | res.status(400).json({ 38 | errors: [{ 39 | message: '文章点赞记录列表获取失败' 40 | }] 41 | }) 42 | userApiLog.error(`commentLikeLog list get fail, ${JSON.stringify(err) 43 | }`) 44 | }) 45 | } 46 | -------------------------------------------------------------------------------- /server/api/blog/event/getEventDetail.js: -------------------------------------------------------------------------------- 1 | const eventUtils = require('../../../mongodb/utils/events') 2 | const utils = require('../../../utils/utils') 3 | const log4js = require('log4js') 4 | const userApiLog = log4js.getLogger('userApi') 5 | 6 | module.exports = async function (req, res, next) { 7 | let { id } = req.query 8 | 9 | const params = { 10 | _id: id, 11 | status: 1, 12 | } 13 | 14 | const rule = [ 15 | { 16 | key: '_id', 17 | label: 'ID', 18 | type: 'isMongoId', 19 | required: true, 20 | }, 21 | 22 | ] 23 | const errors = utils.checkForm(params, rule) 24 | if (errors.length > 0) { 25 | res.status(400).json({ errors }) 26 | return 27 | } 28 | 29 | 30 | 31 | eventUtils.findOne(params).then((data) => { 32 | if (!data) { 33 | res.status(404).json({ 34 | errors: [{ 35 | message: '活动不存在' 36 | }] 37 | }) 38 | return 39 | } 40 | res.send({ 41 | data: data, 42 | }) 43 | 44 | }).catch((err) => { 45 | res.status(400).json({ 46 | errors: [{ 47 | message: '活动详情获取失败' 48 | }] 49 | }) 50 | userApiLog.error(`event get fail, ${JSON.stringify(err) 51 | }`) 52 | }) 53 | } 54 | -------------------------------------------------------------------------------- /server/api/blog/game/getGamePlatformList.js: -------------------------------------------------------------------------------- 1 | const gamePlatformUtils = require('../../../mongodb/utils/gamePlatforms') 2 | const utils = require('../../../utils/utils') 3 | const log4js = require('log4js') 4 | const userApiLog = log4js.getLogger('userApi') 5 | 6 | module.exports = async function (req, res, next) { 7 | const params = { 8 | } 9 | 10 | 11 | const sort = { 12 | _id: -1 13 | } 14 | gamePlatformUtils.find(params, sort, '_id color name').then((data) => { 15 | // 返回格式list,total 16 | res.send({ 17 | data: data, 18 | }) 19 | 20 | }).catch((err) => { 21 | res.status(400).json({ 22 | errors: [{ 23 | message: '游戏平台列表获取失败' 24 | }] 25 | }) 26 | userApiLog.error(`gamePlatform list get fail, ${JSON.stringify(err) 27 | }`) 28 | }) 29 | } 30 | -------------------------------------------------------------------------------- /server/api/blog/game/getPlayingGameList.js: -------------------------------------------------------------------------------- 1 | const cacheDataUtils = require('../../../config/cacheData') 2 | const log4js = require('log4js') 3 | const userApiLog = log4js.getLogger('userApi') 4 | 5 | module.exports = async function (req, res, next) { 6 | if (global.$cacheData?.getPlayingGameList) { 7 | res.send(global.$cacheData.getPlayingGameList) 8 | } else { 9 | cacheDataUtils.getPlayingGameList().then((data) => { 10 | res.send(data) 11 | }).catch((err) => { 12 | res.status(400).json({ 13 | errors: [{ 14 | message: 'getPlayingGameList 列表获取失败' 15 | }] 16 | }) 17 | userApiLog.error(`getPlayingGameList fail, ${JSON.stringify(err)}`) 18 | }) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /server/api/blog/link/getlinkList.js: -------------------------------------------------------------------------------- 1 | const linkUtils = require('../../../mongodb/utils/links') 2 | const utils = require('../../../utils/utils') 3 | const log4js = require('log4js') 4 | const userApiLog = log4js.getLogger('adminApi') 5 | 6 | module.exports = async function (req, res, next) { 7 | const params = { 8 | status: 1 9 | } 10 | const sort = { 11 | taxis: 1, 12 | _id: -1 13 | } 14 | linkUtils.find(params, sort).then((data) => { 15 | // 返回格式list,total 16 | res.send({ 17 | list: data 18 | }) 19 | 20 | }).catch((err) => { 21 | res.status(400).json({ 22 | errors: [{ 23 | message: '友链列表获取失败' 24 | }] 25 | }) 26 | userApiLog.error(`link list get fail, ${JSON.stringify(err) 27 | }`) 28 | }) 29 | } 30 | -------------------------------------------------------------------------------- /server/api/blog/movie/getMovieYearList.js: -------------------------------------------------------------------------------- 1 | const cacheDataUtils = require('../../../config/cacheData') 2 | const log4js = require('log4js') 3 | const userApiLog = log4js.getLogger('userApi') 4 | 5 | module.exports = async function (req, res, next) { 6 | if (global.$cacheData?.movieYearList) { 7 | res.send({ 8 | data: global.$cacheData.movieYearList 9 | }) 10 | } else { 11 | cacheDataUtils.getMovieYearList().then((data) => { 12 | res.send({ 13 | data 14 | }) 15 | }).catch((err) => { 16 | res.status(400).json({ 17 | errors: [{ 18 | message: '电影年份列表获取失败' 19 | }] 20 | }) 21 | userApiLog.error(`movie year list get fail, ${JSON.stringify(err) 22 | }`) 23 | }) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /server/api/blog/navi/getnaviList.js: -------------------------------------------------------------------------------- 1 | const cacheDataUtils = require('../../../config/cacheData') 2 | const log4js = require('log4js') 3 | const userApiLog = log4js.getLogger('userApi') 4 | 5 | module.exports = async function (req, res, next) { 6 | if (global.$cacheData?.naviList) { 7 | res.send({ 8 | data: global.$cacheData.naviList 9 | }) 10 | } else { 11 | cacheDataUtils.getNaviList().then((data) => { 12 | res.send({ 13 | data 14 | }) 15 | }).catch((err) => { 16 | res.status(400).json({ 17 | errors: [{ 18 | message: '导航列表获取失败' 19 | }] 20 | }) 21 | userApiLog.error(`navi list get fail, ${JSON.stringify(err) 22 | }`) 23 | }) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /server/api/blog/option/getoptionList.js: -------------------------------------------------------------------------------- 1 | const optionUtils = require('../../../mongodb/utils/options') 2 | const utils = require('../../../utils/utils') 3 | const log4js = require('log4js') 4 | const userApiLog = log4js.getLogger('userApi') 5 | 6 | module.exports = async function (req, res, next) { 7 | 8 | if (!global.$globalConfig?.siteSettings || !global.$globalConfig?.commentSettings) { 9 | // 400 10 | res.status(400).json({ 11 | errors: [{ 12 | message: '获取配置失败' 13 | }] 14 | }) 15 | return 16 | } 17 | 18 | res.send({ 19 | data: { 20 | ...global.$globalConfig.siteSettings, 21 | ...global.$globalConfig.commentSettings, 22 | ...global.$globalConfig.rssSettings, 23 | ...global.$globalConfig.sitePostSettings, 24 | ...global.$globalConfig.adSettings, 25 | } 26 | }) 27 | } 28 | -------------------------------------------------------------------------------- /server/api/blog/post/getPostArchiveList.js: -------------------------------------------------------------------------------- 1 | const cacheDataUtils = require('../../../config/cacheData') 2 | const log4js = require('log4js') 3 | const userApiLog = log4js.getLogger('userApi') 4 | 5 | module.exports = async function (req, res, next) { 6 | if (global.$cacheData?.postArchiveList) { 7 | res.send(global.$cacheData.postArchiveList) 8 | } else { 9 | cacheDataUtils.getPostArchiveList().then((data) => { 10 | res.send(data) 11 | }).catch((err) => { 12 | res.status(400).json({ 13 | errors: [{ 14 | message: 'postArchive列表获取失败' 15 | }] 16 | }) 17 | userApiLog.error(`postArchive list get fail, ${JSON.stringify(err) 18 | }`) 19 | }) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /server/api/blog/postLikeLog/getPostLikeLogList.js: -------------------------------------------------------------------------------- 1 | const postLikeLogUtils = require('../../../mongodb/utils/postLikeLogs') 2 | const utils = require('../../../utils/utils') 3 | const log4js = require('log4js') 4 | const userApiLog = log4js.getLogger('userApi') 5 | 6 | module.exports = async function (req, res, next) { 7 | // 获取id列表 8 | const postIdList = req.body.postIdList 9 | const uuid = req.headers['wmb-request-id'] 10 | const ip = utils.getUserIp(req) 11 | // 判断uuid是否符合格式 12 | if (!utils.isUUID(uuid)) { 13 | res.status(400).json({ 14 | errors: [{ 15 | message: '参数错误' 16 | }] 17 | }) 18 | return 19 | } 20 | 21 | const params = { 22 | post: { 23 | $in: postIdList 24 | }, 25 | // 判断uuid 26 | uuid, 27 | } 28 | 29 | const sort = { 30 | _id: -1 31 | } 32 | postLikeLogUtils.find(params, sort, '_id post like __v').then((data) => { 33 | res.send({ 34 | list: data 35 | }) 36 | }).catch((err) => { 37 | res.status(400).json({ 38 | errors: [{ 39 | message: '文章点赞记录列表获取失败' 40 | }] 41 | }) 42 | userApiLog.error(`postLikeLog list get fail, ${JSON.stringify(err) 43 | }`) 44 | }) 45 | } 46 | -------------------------------------------------------------------------------- /server/api/blog/sidebar/getSidebarList.js: -------------------------------------------------------------------------------- 1 | const cacheDataUtils = require('../../../config/cacheData') 2 | const log4js = require('log4js') 3 | const userApiLog = log4js.getLogger('userApi') 4 | 5 | module.exports = async function (req, res, next) { 6 | if (global.$cacheData?.sidebarList) { 7 | res.send(global.$cacheData.sidebarList) 8 | } else { 9 | cacheDataUtils.getSidebarList().then((data) => { 10 | res.send(data) 11 | }).catch((err) => { 12 | res.status(400).json({ 13 | errors: [{ 14 | message: 'sidebar列表获取失败' 15 | }] 16 | }) 17 | userApiLog.error(`sidebar list get fail, ${JSON.stringify(err) 18 | }`) 19 | }) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /server/api/blog/sort/getSortList.js: -------------------------------------------------------------------------------- 1 | const cacheDataUtils = require('../../../config/cacheData') 2 | const log4js = require('log4js') 3 | const userApiLog = log4js.getLogger('userApi') 4 | 5 | module.exports = async function (req, res, next) { 6 | if (global.$cacheData?.sortList) { 7 | res.send(global.$cacheData.sortList) 8 | } else { 9 | cacheDataUtils.getSortList().then((data) => { 10 | res.send(data) 11 | }).catch((err) => { 12 | res.status(400).json({ 13 | errors: [{ 14 | message: 'sort列表获取失败' 15 | }] 16 | }) 17 | userApiLog.error(`sort list get fail, ${JSON.stringify(err) 18 | }`) 19 | }) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /server/api/blog/tag/getTagDetail.js: -------------------------------------------------------------------------------- 1 | const tagUtils = require('../../../mongodb/utils/tags') 2 | const utils = require('../../../utils/utils') 3 | const log4js = require('log4js') 4 | const userApiLog = log4js.getLogger('userApi') 5 | 6 | module.exports = async function (req, res, next) { 7 | const id = req.query.id 8 | if (!id) { 9 | res.status(400).json({ 10 | errors: [{ 11 | message: 'id不能为空' 12 | }] 13 | }) 14 | return 15 | } 16 | // findOne 17 | tagUtils.findOne({ _id: id }, '_id tagname').then((data) => { 18 | if (!data) { 19 | res.status(404).json({ 20 | errors: [{ 21 | message: '标签不存在' 22 | }] 23 | }) 24 | return 25 | } 26 | res.send({ 27 | data: data 28 | }) 29 | }).catch((err) => { 30 | res.status(400).json({ 31 | errors: [{ 32 | message: '标签详情获取失败' 33 | }] 34 | }) 35 | userApiLog.error(`tag detail get fail, ${logErrorToText(err)}`) 36 | }) 37 | } 38 | -------------------------------------------------------------------------------- /server/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | LOCK_FILE="/app/log/install.lock" 4 | function start() { 5 | echo "Starting service" 6 | yarn run start 7 | } 8 | function init() { 9 | echo "Initializing user: $USER_NAME" 10 | yarn run create-user "$USER_NAME" '7@wVUo6BL6LHjNR*#x' "$USER_NAME" 11 | # 检查/app/log目录是否存在,如果不存在,则创建它 12 | if [ ! -d "/app/log" ]; then 13 | mkdir -p "/app/log" 14 | fi 15 | touch "$LOCK_FILE" 16 | } 17 | if [ -n "$USER_NAME" ]; then 18 | 19 | if [ ! -f "$LOCK_FILE" ]; then 20 | init 21 | fi 22 | fi 23 | start -------------------------------------------------------------------------------- /server/front/README.md: -------------------------------------------------------------------------------- 1 | 前端打包会生成到这个目录 2 | -------------------------------------------------------------------------------- /server/mongodb/models/albums.js: -------------------------------------------------------------------------------- 1 | var mongoose = require('mongoose'); 2 | var Schema = mongoose.Schema; 3 | // Schema 4 | var albums = new Schema({ 5 | name: { 6 | type: String, 7 | required: true, 8 | // 唯一 9 | unique: true 10 | }, 11 | // 相册下的文件数量 12 | count: { 13 | type: Number, 14 | default: 0, 15 | index: true 16 | }, 17 | }, { timestamps: true }); 18 | 19 | module.exports = mongoose.model('albums', albums); -------------------------------------------------------------------------------- /server/mongodb/models/backups.js: -------------------------------------------------------------------------------- 1 | var mongoose = require('mongoose'); 2 | var Schema = mongoose.Schema; 3 | // Schema 4 | var backups = new Schema({ 5 | // 备份名称 6 | name: { 7 | type: String, 8 | required: true 9 | }, 10 | // 备份类型, 1: 备份,2: 还原 11 | type: { 12 | type: Number, 13 | required: true, 14 | index: true 15 | }, 16 | // 备份文件名 17 | filename: { 18 | type: String, 19 | default: '' 20 | }, 21 | // 备份文件状态,0: 备份中,1: 未删除, 2: 已删除, 3: 上传尚未完成 22 | fileStatus: { 23 | type: Number, 24 | required: true, 25 | default: 0, 26 | index: true 27 | }, 28 | // 备份文件大小 29 | fileSize: { 30 | type: Number, 31 | default: null 32 | }, 33 | // 备份状态,0: 备份中/还原中,1: 成功,2: 失败, 3: 上传尚未完成 34 | status: { 35 | type: Number, 36 | required: true, 37 | default: 0, 38 | index: true 39 | }, 40 | // 原因 41 | reason: { 42 | type: String, 43 | default: '' 44 | }, 45 | // 备份备注 46 | remark: { 47 | type: String, 48 | default: '' 49 | } 50 | }, { timestamps: true }); 51 | 52 | module.exports = mongoose.model('backups', backups); -------------------------------------------------------------------------------- /server/mongodb/models/banners.js: -------------------------------------------------------------------------------- 1 | var mongoose = require('mongoose'); 2 | var Schema = mongoose.Schema; 3 | // Schema 4 | var banners = new Schema({ 5 | // banner名称 6 | title: { type: String }, 7 | // 排序 8 | taxis: { type: Number, default: 0, index: true }, 9 | // banner图片 10 | img: { type: String }, 11 | imgPath: { type: String, default: null }, 12 | // link 13 | link: { type: String }, 14 | // 是否新窗口打开 15 | newtab: { 16 | type: Boolean, 17 | default: false 18 | }, 19 | // 是否本站链接 20 | isdefault: { 21 | type: Boolean, 22 | default: false 23 | }, 24 | // 0:不可见 1:可见 25 | status: { type: Number, default: 0, index: true }, 26 | }, { timestamps: true }); 27 | 28 | module.exports = mongoose.model('banners', banners); -------------------------------------------------------------------------------- /server/mongodb/models/booktypes.js: -------------------------------------------------------------------------------- 1 | var mongoose = require('mongoose'); 2 | var Schema = mongoose.Schema; 3 | // Schema 4 | var booktypes = new Schema({ 5 | name: { type: String, required: true }, 6 | color: { type: String, required: true }, 7 | }, { timestamps: true }); 8 | 9 | module.exports = mongoose.model('booktypes', booktypes); -------------------------------------------------------------------------------- /server/mongodb/models/commentLikeLogs.js: -------------------------------------------------------------------------------- 1 | var mongoose = require('mongoose'); 2 | var Schema = mongoose.Schema; 3 | // Schema 4 | var commentLikeLogs = new Schema({ 5 | comment: { 6 | type: mongoose.Schema.Types.ObjectId, 7 | ref: 'comments', 8 | required: true, 9 | index: true 10 | }, 11 | // 操作者的uuid 12 | uuid: { 13 | type: String, 14 | required: true, 15 | index: true 16 | }, 17 | // 点赞还是取消点赞 18 | like: { 19 | type: Boolean, 20 | required: true, 21 | index: true 22 | }, 23 | // date 24 | date: { 25 | type: Date, 26 | expires: 31968000, 27 | default: Date.now, 28 | index: true 29 | }, 30 | // 评论者ip 31 | ip: { 32 | type: String, 33 | default: '', 34 | index: true 35 | }, 36 | ipInfo: { 37 | type: Object, 38 | default: {} 39 | }, 40 | deviceInfo: { 41 | type: Object, 42 | default: {} 43 | }, 44 | }, { timestamps: true }); 45 | 46 | module.exports = mongoose.model('commentLikeLogs', commentLikeLogs); -------------------------------------------------------------------------------- /server/mongodb/models/emailSendHistorys.js: -------------------------------------------------------------------------------- 1 | var mongoose = require('mongoose'); 2 | var Schema = mongoose.Schema; 3 | // Schema 4 | var emailSendHistorys = new Schema({ 5 | // 发送对象 6 | to: { 7 | type: String, 8 | }, 9 | subject: { 10 | type: String, 11 | }, 12 | // 发送内容 13 | content: { 14 | type: String, 15 | }, 16 | errInfo: { 17 | type: String, 18 | }, 19 | // 发送状态 0: 发送失败 1: 发送成功 20 | status: { 21 | type: Number, 22 | default: 0, 23 | index: true 24 | }, 25 | }, { capped: 15728640, timestamps: true }); 26 | 27 | module.exports = mongoose.model('emailSendHistorys', emailSendHistorys); -------------------------------------------------------------------------------- /server/mongodb/models/events.js: -------------------------------------------------------------------------------- 1 | var mongoose = require('mongoose'); 2 | var Schema = mongoose.Schema; 3 | // Schema 4 | var events = new Schema({ 5 | eventtype: { 6 | type: Schema.Types.ObjectId, 7 | ref: 'eventtypes', 8 | required: true, 9 | index: true 10 | }, 11 | // 标题 12 | title: { 13 | type: String, 14 | required: true 15 | }, 16 | color: { 17 | type: String, 18 | default: null 19 | }, 20 | urlList: { 21 | type: [ 22 | { 23 | text: String, 24 | url: String 25 | } 26 | ], 27 | default: [] 28 | }, 29 | content: { 30 | type: String, 31 | default: '' 32 | }, 33 | // 开始时间 34 | startTime: { 35 | type: Date, 36 | required: true, 37 | index: true 38 | }, 39 | // 结束时间 40 | endTime: { 41 | type: Date, 42 | required: true, 43 | index: true 44 | }, 45 | // 状态 0: 不显示 1: 显示 46 | status: { 47 | type: Number, 48 | default: 0, 49 | index: true 50 | }, 51 | }, { timestamps: true }); 52 | 53 | module.exports = mongoose.model('events', events); -------------------------------------------------------------------------------- /server/mongodb/models/eventtypes.js: -------------------------------------------------------------------------------- 1 | var mongoose = require('mongoose'); 2 | var Schema = mongoose.Schema; 3 | // Schema 4 | var eventtypes = new Schema({ 5 | name: { type: String, required: true }, 6 | color: { type: String, required: true }, 7 | }, { timestamps: true }); 8 | 9 | module.exports = mongoose.model('eventtypes', eventtypes); -------------------------------------------------------------------------------- /server/mongodb/models/gamePlatforms.js: -------------------------------------------------------------------------------- 1 | var mongoose = require('mongoose'); 2 | var Schema = mongoose.Schema; 3 | // Schema 4 | var gamePlatforms = new Schema({ 5 | name: { type: String, required: true }, 6 | color: { type: String, required: true }, 7 | }, { timestamps: true }); 8 | 9 | module.exports = mongoose.model('gamePlatforms', gamePlatforms); -------------------------------------------------------------------------------- /server/mongodb/models/links.js: -------------------------------------------------------------------------------- 1 | var mongoose = require('mongoose'); 2 | var Schema = mongoose.Schema; 3 | // Schema 4 | var links = new Schema({ 5 | // icon 图标 6 | icon: { 7 | type: String, 8 | default: '' 9 | }, 10 | iconPath: { 11 | type: String, 12 | default: null 13 | }, 14 | // sitename 网站名称字段 15 | sitename: { 16 | type: String, 17 | default: '', 18 | // 必填 19 | required: true 20 | }, 21 | // siteurl 网站URL字段 22 | siteurl: { 23 | type: String, 24 | default: '', 25 | // 必填 26 | required: true 27 | }, 28 | // RSS地址 29 | rss: { 30 | type: String, 31 | default: '', 32 | }, 33 | // description 描述字段 34 | description: { 35 | type: String, 36 | default: '' 37 | }, 38 | // taxis 排序字段 39 | taxis: { 40 | type: Number, 41 | default: 0, 42 | index: true 43 | }, 44 | // status 状态字段:0不显示,1显示 45 | status: { 46 | type: Number, 47 | default: 1, 48 | index: true 49 | }, 50 | }, { timestamps: true }); 51 | 52 | module.exports = mongoose.model('links', links); -------------------------------------------------------------------------------- /server/mongodb/models/movies.js: -------------------------------------------------------------------------------- 1 | var mongoose = require('mongoose'); 2 | var Schema = mongoose.Schema; 3 | // Schema 4 | var movies = new Schema({ 5 | // 标题 6 | title: { 7 | type: String, 8 | }, 9 | // 封面 10 | cover: { 11 | type: String, 12 | }, 13 | coverFolder: { 14 | type: String, 15 | default: null 16 | }, 17 | coverFileName: { 18 | type: String, 19 | default: null 20 | }, 21 | // 简评 22 | summary: { 23 | type: String, 24 | }, 25 | // 评分,神作,佳作,良作,劣作,烂作,迷 26 | rating: { 27 | type: Number, 28 | index: true 29 | }, 30 | // 年份 31 | year: { 32 | type: Number, 33 | index: true 34 | }, 35 | // 月份 36 | month: { 37 | type: Number, 38 | index: true 39 | }, 40 | // 日期 41 | day: { 42 | type: Number, 43 | index: true 44 | }, 45 | urlList: { 46 | type: [ 47 | { 48 | text: String, 49 | url: String 50 | } 51 | ], 52 | default: [] 53 | }, 54 | // label 字符串数组 55 | label: { 56 | type: [String], 57 | default: [] 58 | }, 59 | // 状态 0: 不显示 1: 显示 60 | status: { 61 | type: Number, 62 | default: 0, 63 | index: true 64 | }, 65 | }, { timestamps: true }); 66 | 67 | module.exports = mongoose.model('movies', movies); -------------------------------------------------------------------------------- /server/mongodb/models/navis.js: -------------------------------------------------------------------------------- 1 | var mongoose = require('mongoose'); 2 | var Schema = mongoose.Schema; 3 | // Schema 4 | var navis = new Schema({ 5 | naviname: { 6 | type: String, 7 | required: true, 8 | }, 9 | url: { 10 | type: String, 11 | }, 12 | newtab: { 13 | type: Boolean, 14 | default: false 15 | }, 16 | // 状态 0 不显示 1 显示 17 | status: { 18 | type: Number, 19 | default: 0, 20 | index: true 21 | }, 22 | // 排序 23 | taxis: { 24 | type: Number, 25 | default: 0, 26 | index: true 27 | }, 28 | // 父导航 29 | parent: { 30 | type: Schema.Types.ObjectId, 31 | ref: 'navis', 32 | index: true 33 | }, 34 | // 是否本站链接 35 | isdefault: { 36 | type: Boolean, 37 | default: false 38 | }, 39 | // query 40 | query: { 41 | type: String, 42 | default: '' 43 | }, 44 | deepmatch: { 45 | type: Boolean, 46 | default: false 47 | } 48 | }, { timestamps: true }); 49 | 50 | module.exports = mongoose.model('navis', navis); -------------------------------------------------------------------------------- /server/mongodb/models/options.js: -------------------------------------------------------------------------------- 1 | var mongoose = require('mongoose'); 2 | var Schema = mongoose.Schema; 3 | // Schema 4 | var options = new Schema({ 5 | name: { 6 | type: String, 7 | required: true, 8 | index: true 9 | }, 10 | // value 11 | value: { 12 | type: String, 13 | }, 14 | }, { timestamps: true }); 15 | 16 | module.exports = mongoose.model('options', options); -------------------------------------------------------------------------------- /server/mongodb/models/postLikeLogs.js: -------------------------------------------------------------------------------- 1 | var mongoose = require('mongoose'); 2 | var Schema = mongoose.Schema; 3 | // Schema 4 | var postLikeLogs = new Schema({ 5 | post: { 6 | type: mongoose.Schema.Types.ObjectId, 7 | ref: 'posts', 8 | required: true, 9 | index: true 10 | }, 11 | // 操作者的uuid 12 | uuid: { 13 | type: String, 14 | required: true, 15 | }, 16 | // 点赞还是取消点赞 17 | like: { 18 | type: Boolean, 19 | required: true, 20 | }, 21 | // date 22 | date: { 23 | type: Date, 24 | expires: 31968000, 25 | default: Date.now 26 | }, 27 | // 评论者ip 28 | ip: { 29 | type: String, 30 | default: '' 31 | }, 32 | ipInfo: { 33 | type: Object, 34 | default: {} 35 | }, 36 | deviceInfo: { 37 | type: Object, 38 | default: {} 39 | }, 40 | }, { timestamps: true }); 41 | 42 | module.exports = mongoose.model('postLikeLogs', postLikeLogs); -------------------------------------------------------------------------------- /server/mongodb/models/referrers.js: -------------------------------------------------------------------------------- 1 | var mongoose = require('mongoose'); 2 | var Schema = mongoose.Schema; 3 | // Schema 4 | var referrers = new Schema({ 5 | // 来源 6 | referrer: { 7 | type: String, 8 | required: true, 9 | }, 10 | // 来源类型 11 | referrerType: { 12 | type: String, 13 | required: true, 14 | }, 15 | }, { capped: 15728640, timestamps: true }); 16 | 17 | module.exports = mongoose.model('referrers', referrers); -------------------------------------------------------------------------------- /server/mongodb/models/rsslogs.js: -------------------------------------------------------------------------------- 1 | var mongoose = require('mongoose'); 2 | var Schema = mongoose.Schema; 3 | // Schema 4 | var rsslogs = new Schema({ 5 | ip: { 6 | type: String, 7 | }, 8 | ipInfo: { 9 | type: Object, 10 | default: {} 11 | }, 12 | deviceInfo: { 13 | type: Object, 14 | default: {} 15 | }, 16 | rssPath: { 17 | type: String, 18 | default: '' 19 | }, 20 | reader: { 21 | type: String, 22 | default: '' 23 | }, 24 | }, { capped: 15728640, timestamps: true }); 25 | 26 | module.exports = mongoose.model('rsslogs', rsslogs); -------------------------------------------------------------------------------- /server/mongodb/models/sidebars.js: -------------------------------------------------------------------------------- 1 | var mongoose = require('mongoose'); 2 | var Schema = mongoose.Schema; 3 | // Schema 4 | var sidebars = new Schema({ 5 | // 侧边栏名称 6 | title: { type: String }, 7 | // 侧边栏内容 8 | content: { type: String }, 9 | // 计数 10 | count: { type: Number, default: 1 }, 11 | // 1:自定义 3:最新评论 4:随机标签 5:随机文章 6:相册 8:分类 9:归档 10:谷歌广告 12 | type: { 13 | type: Number, 14 | // 必填 15 | required: true, 16 | index: true 17 | }, 18 | // 排序 19 | taxis: { type: Number, default: 0, index: true }, 20 | // 0:不可见 1:可见 21 | status: { type: Number, default: 0, index: true }, 22 | }, { timestamps: true }); 23 | 24 | module.exports = mongoose.model('sidebars', sidebars); -------------------------------------------------------------------------------- /server/mongodb/models/sorts.js: -------------------------------------------------------------------------------- 1 | var mongoose = require('mongoose'); 2 | var Schema = mongoose.Schema; 3 | // Schema 4 | var sorts = new Schema({ 5 | sortname: { 6 | type: String, 7 | required: true, 8 | }, 9 | // alias 别名 10 | alias: { 11 | type: String, 12 | index: true 13 | }, 14 | // taxis 排序 15 | taxis: { 16 | type: Number, 17 | default: 0, 18 | index: true 19 | }, 20 | // 父级分类 ObjectId 或者null 21 | parent: { 22 | type: Schema.Types.ObjectId, 23 | ref: 'sorts', 24 | index: true 25 | }, 26 | // description 描述 27 | description: { 28 | type: String, 29 | }, 30 | // template 模板 31 | template: { 32 | type: String, 33 | }, 34 | }, { timestamps: true }); 35 | 36 | module.exports = mongoose.model('sorts', sorts); -------------------------------------------------------------------------------- /server/mongodb/models/tags.js: -------------------------------------------------------------------------------- 1 | var mongoose = require('mongoose'); 2 | var Schema = mongoose.Schema; 3 | // Schema 4 | var tags = new Schema({ 5 | tagname: { 6 | type: String, 7 | required: true, 8 | }, 9 | // 最后一次使用时间 10 | lastusetime: { 11 | type: Date, 12 | default: Date.now 13 | }, 14 | }, { timestamps: true }); 15 | 16 | module.exports = mongoose.model('tags', tags); -------------------------------------------------------------------------------- /server/mongodb/models/users.js: -------------------------------------------------------------------------------- 1 | var mongoose = require('mongoose'); 2 | var Schema = mongoose.Schema; 3 | // Schema 4 | var users = new Schema({ 5 | username: { 6 | type: String, 7 | required: true, 8 | unique: true, 9 | index: true 10 | }, 11 | password: { 12 | type: String, 13 | required: true, 14 | }, 15 | nickname: { 16 | type: String, 17 | required: true, 18 | }, 19 | role: { 20 | type: Number, 21 | default: 1, 22 | index: true 23 | }, 24 | photo: String, 25 | email: String, 26 | description: String, 27 | cover: { type: Schema.ObjectId, ref: 'attachments', default: null }, 28 | disabled: { 29 | type: Boolean, 30 | default: false, 31 | index: true 32 | }, 33 | pwversion: { 34 | type: Number, 35 | default: 0, 36 | index: true 37 | }, 38 | IP: String, 39 | ipInfo: { 40 | type: Object, 41 | default: {} 42 | }, 43 | }, { timestamps: true }); 44 | 45 | module.exports = mongoose.model('users', users); -------------------------------------------------------------------------------- /server/mongodb/models/votelogs.js: -------------------------------------------------------------------------------- 1 | var mongoose = require('mongoose'); 2 | var Schema = mongoose.Schema; 3 | // Schema 4 | var votelogs = new Schema({ 5 | vote: { 6 | type: mongoose.Schema.Types.ObjectId, 7 | ref: 'votes', 8 | required: true, 9 | index: true 10 | }, 11 | post: { 12 | type: mongoose.Schema.Types.ObjectId, 13 | ref: 'posts', 14 | index: true, 15 | default: null 16 | }, 17 | // 选项 数组 18 | options: [{ 19 | type: mongoose.Schema.Types.ObjectId, 20 | required: true, 21 | }], 22 | 23 | // 操作者的uuid 24 | uuid: { 25 | type: String, 26 | required: true, 27 | }, 28 | // ip 29 | ip: { 30 | type: String, 31 | index: true 32 | }, 33 | ipInfo: { 34 | type: Object, 35 | default: {} 36 | }, 37 | deviceInfo: { 38 | type: Object, 39 | default: {} 40 | }, 41 | expireAt: { 42 | type: Date, 43 | expires: 31968000, 44 | default: Date.now, 45 | index: true 46 | } 47 | }, { timestamps: true }); 48 | 49 | module.exports = mongoose.model('votelogs', votelogs); -------------------------------------------------------------------------------- /server/public/content/uploadfile/README.md: -------------------------------------------------------------------------------- 1 | 相册目录 2 | -------------------------------------------------------------------------------- /server/public/ucloudImg/README.md: -------------------------------------------------------------------------------- 1 | 兼容老站资源 2 | -------------------------------------------------------------------------------- /server/public/up_works/README.md: -------------------------------------------------------------------------------- 1 | 兼容老站资源 2 | -------------------------------------------------------------------------------- /server/public/upload/avatar/README.md: -------------------------------------------------------------------------------- 1 | 头像目录 2 | -------------------------------------------------------------------------------- /server/public/upload/bangumi/README.md: -------------------------------------------------------------------------------- 1 | 放 追番的图片 2 | -------------------------------------------------------------------------------- /server/public/upload/banner/README.md: -------------------------------------------------------------------------------- 1 | banner 目录 2 | -------------------------------------------------------------------------------- /server/public/upload/linkicon/README.md: -------------------------------------------------------------------------------- 1 | 友链图标目录 2 | -------------------------------------------------------------------------------- /server/public/upload/siteImg/README.md: -------------------------------------------------------------------------------- 1 | 放 网站设定的图片 2 | -------------------------------------------------------------------------------- /server/public/web_demo/README.md: -------------------------------------------------------------------------------- 1 | 兼容老站资源 2 | -------------------------------------------------------------------------------- /server/sample.env: -------------------------------------------------------------------------------- 1 | PORT=3000 2 | DB_HOST=mongodb://mongodb:27017 3 | JSON_LIMIT=50mb 4 | URLENCODED_LIMIT=50mb 5 | IP2LOCATION_FILE_NAME= 6 | MAX_HISTORYLOGS_SIZE=1073741824 -------------------------------------------------------------------------------- /server/seo/rss/README.md: -------------------------------------------------------------------------------- 1 | 放置 RSS 的 xml 文件 2 | -------------------------------------------------------------------------------- /server/tools/backupFromEmlogJson/README.md: -------------------------------------------------------------------------------- 1 | 这里存放 emlog 的备份文件 2 | -------------------------------------------------------------------------------- /server/tools/cacheTest.js: -------------------------------------------------------------------------------- 1 | const http = require('http'); 2 | const crypto = require('crypto'); 3 | 4 | // 定义并发请求的数量 5 | const CONCURRENT_REQUESTS = 1000; 6 | let requestCount = 0; 7 | 8 | // 创建一个函数来发送请求 9 | function sendRequest () { 10 | // 生成一个随机字符串 11 | const randomString = crypto.randomBytes(16).toString('hex'); 12 | const timestamp = Date.now(); 13 | console.log(`Sending request with query: ${randomString}, timestamp: ${timestamp}`); 14 | 15 | // 创建一个请求 16 | return new Promise((resolve, reject) => { 17 | http.get(`http://localhost:3007/?query=${randomString}×tamp=${timestamp}`, (res) => { 18 | let data = ''; 19 | res.on('data', chunk => data += chunk); 20 | res.on('end', () => resolve(data)); 21 | }).on('error', reject); 22 | }); 23 | } 24 | 25 | // 每10毫秒发送一次请求 26 | const intervalId = setInterval(() => { 27 | if (requestCount >= CONCURRENT_REQUESTS) { 28 | clearInterval(intervalId); 29 | } else { 30 | sendRequest() 31 | .then(response => { 32 | // 请求成功 33 | console.log(`Request was successful.`); 34 | }) 35 | .catch(error => { 36 | // 请求失败 37 | console.error(`Request failed: ${error.message}`); 38 | }); 39 | requestCount++; 40 | } 41 | }, 300); -------------------------------------------------------------------------------- /server/tools/deleteEmailSmtpSslOption.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config() 2 | const chalk = require('chalk') 3 | const db = require('./mongodb') 4 | const optionUtils = require('../mongodb/utils/options') 5 | 6 | 7 | const init = async () => { 8 | // 删除name为emailSmtpSsl 的数据 9 | console.log(chalk.green('删除emailSmtpSsl')) 10 | const res1 = await optionUtils.deleteMany({ 11 | name: 'emailSmtpSsl' 12 | }) 13 | console.log(chalk.green('删除emailSmtpSsl成功', JSON.stringify(res1))) 14 | // 关闭 15 | process.exit(0) 16 | } 17 | 18 | db.once('open', () => { 19 | init() 20 | }) 21 | -------------------------------------------------------------------------------- /server/tools/deleteOldSitePostCommonFooterContent.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config() 2 | const chalk = require('chalk') 3 | const db = require('./mongodb') 4 | const postsUtil = require('../mongodb/utils/posts'); 5 | const optionUtils = require('../mongodb/utils/options') 6 | 7 | 8 | const init = async () => { 9 | // 删除name为sitePostCommonFooterContent 和 sitePostCommonFooterContentIsRichMode 的数据 10 | console.log(chalk.green('删除旧的站点文章底部内容')) 11 | const res1 = await optionUtils.deleteMany({ 12 | name: 'sitePostCommonFooterContent' 13 | }) 14 | console.log(chalk.green('删除sitePostCommonFooterContent成功', JSON.stringify(res1))) 15 | const res2 = await optionUtils.deleteMany({ 16 | name: 'sitePostCommonFooterContentIsRichMode' 17 | }) 18 | console.log(chalk.green('删除sitePostCommonFooterContentIsRichMode成功', JSON.stringify(res2))) 19 | console.log(chalk.green('删除旧的站点文章底部内容成功')) 20 | // 关闭 21 | process.exit(0) 22 | } 23 | 24 | db.once('open', () => { 25 | init() 26 | }) 27 | -------------------------------------------------------------------------------- /server/tools/fixNavisQuery.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config() 2 | const chalk = require('chalk') 3 | const db = require('./mongodb') 4 | const navisUtils = require('../mongodb/utils/navis'); 5 | 6 | const init = async () => { 7 | console.log(chalk.green('开始修复导航query字段')) 8 | // 查找所有 query 字段为对象类型的文档 9 | const docs = await navisUtils.find({ 10 | query: { $type: 'object' } 11 | }, undefined, undefined, { lean: true }); 12 | console.log(`找到 ${docs.length} 个需要更新的文档`); 13 | 14 | // 逐个更新文档 15 | for (const doc of docs) { 16 | if (doc.query && typeof doc.query === 'object') { 17 | // 将对象转换为字符串 18 | const queryString = ''; 19 | 20 | // 更新该文档 21 | await navisUtils.updateOne( 22 | { _id: doc._id }, 23 | { $set: { query: queryString } }, 24 | { 25 | strict: false, // 忽略架构验证 26 | new: true // 返回更新后的文档 27 | } 28 | ); 29 | } 30 | } 31 | 32 | console.log('更新完成'); 33 | process.exit(0) 34 | } 35 | 36 | db.once('open', () => { 37 | init() 38 | }) -------------------------------------------------------------------------------- /server/tools/getUserList.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config() 2 | const chalk = require('chalk') 3 | const db = require('./mongodb') 4 | require('../mongodb/utils/attachments') 5 | const userUtils = require('../mongodb/utils/users') 6 | const utils = require('../utils/utils') 7 | 8 | const init = async () => { 9 | const res = await userUtils.find({}) 10 | // 打印成table,只输出username和disabled 11 | console.table(res.map(item => { 12 | return { 13 | username: item.username, 14 | disabled: item.disabled 15 | } 16 | })) 17 | // 关闭 18 | process.exit(0) 19 | } 20 | 21 | db.once('open', () => { 22 | init() 23 | }) -------------------------------------------------------------------------------- /server/tools/setUserBan.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config() 2 | const chalk = require('chalk') 3 | const username = process.argv[2] 4 | if (!username) { 5 | console.log(chalk.red('用户名不能为空')) 6 | return 7 | } 8 | const db = require('./mongodb') 9 | require('../mongodb/utils/attachments') 10 | const userUtils = require('../mongodb/utils/users') 11 | const utils = require('../utils/utils') 12 | 13 | const checkAdmin = async () => { 14 | console.log('checking...') 15 | const res = await userUtils.findOne({ username: username }) 16 | if (res) { 17 | return res 18 | } else { 19 | console.log(chalk.red('用户不存在')) 20 | // 关闭进程 21 | process.exit(0) 22 | } 23 | } 24 | 25 | const setBan = async () => { 26 | const isBan = process.argv[3] === '1' ? true : false 27 | const res = await userUtils.updateOne({ username: username }, { disabled: isBan }) 28 | if (res.modifiedCount > 0) { 29 | const text = isBan ? '禁用' : '解禁' 30 | console.log(chalk.green(`${username}${text}成功`)) 31 | } else { 32 | console.log(chalk.red('设置失败')) 33 | } 34 | // 关闭进程 35 | process.exit(0) 36 | } 37 | 38 | const init = async () => { 39 | await checkAdmin() 40 | await setBan() 41 | } 42 | 43 | db.once('open', () => { 44 | init() 45 | }) -------------------------------------------------------------------------------- /server/tools/sumPostComment.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | const db = require('./mongodb'); 3 | const commentUtils = require('../mongodb/utils/comments'); 4 | const postUtils = require('../mongodb/utils/posts'); 5 | 6 | async function fixCommentCountForPost (post) { 7 | // 获取 Post 下面 status 为 1 的评论数 8 | const commentCount = await commentUtils.count({ post: post._id, status: 1 }); 9 | console.log(`Post ${post.title || post.excerpt} has ${commentCount} comments.`); 10 | 11 | // 更新 Post 的 comnum 12 | await postUtils.updateOne({ _id: post._id }, { comnum: commentCount }); 13 | console.log(`Post ${post.title} updated.`); 14 | } 15 | 16 | async function fixCommentCount () { 17 | // 获取所有 Post 18 | console.log('Finding all posts...'); 19 | const posts = await postUtils.find(); 20 | 21 | // 使用 Promise.all 并行处理所有的 Post 22 | await Promise.all(posts.map(fixCommentCountForPost)); 23 | 24 | console.log('Comment count fixed for all posts.'); 25 | } 26 | 27 | // 连接到数据库并修复评论数 28 | db.once('open', () => { 29 | fixCommentCount().then(() => { 30 | console.log('Done'); 31 | process.exit(0); 32 | }).catch((err) => { 33 | console.error(err); 34 | process.exit(1); 35 | }); 36 | }) -------------------------------------------------------------------------------- /server/utils/ip2location/README.md: -------------------------------------------------------------------------------- 1 | 如果需要 IP 解析请先从:https://lite.ip2location.com 下载 IP2LOCATION-LITE-DB1.BIN 文件,然后放到 utils/ip2location 目录下 2 | -------------------------------------------------------------------------------- /server/utils/workers/restoreWorker.js: -------------------------------------------------------------------------------- 1 | const { parentPort } = require('worker_threads'); 2 | const backupTools = require('../backup'); 3 | const db = require('../../tools/mongodb'); 4 | 5 | const dbPromise = new Promise((resolve, reject) => { 6 | db.once('open', () => { 7 | resolve(); 8 | }); 9 | db.on('error', (err) => { 10 | reject(err); 11 | }); 12 | }); 13 | 14 | parentPort.on('message', async (fullPath) => { 15 | dbPromise.then(async () => { 16 | console.log('worker start'); 17 | await backupTools.unzipBackup(fullPath); 18 | await backupTools.clearCollections(); 19 | await backupTools.restoreCollections(fullPath); 20 | await backupTools.removePublicContents() 21 | await backupTools.restorePublic(fullPath) 22 | await backupTools.clearRestoreCache(fullPath) 23 | parentPort.postMessage({ status: 'success' }); 24 | parentPort.close(); 25 | }).catch((err) => { 26 | console.error('Failed to connect to database', err); 27 | parentPort.postMessage({ status: 'error', error: err }); 28 | parentPort.close(); 29 | }); 30 | }); -------------------------------------------------------------------------------- /ver-change.js: -------------------------------------------------------------------------------- 1 | const { execSync } = require('child_process'); 2 | 3 | // 获取命令行参数中的版本号 4 | const newVersion = process.argv[2]; 5 | 6 | if (!newVersion) { 7 | console.error('请指定新版本号,例如: yarn run ver-change 0.1.0'); 8 | process.exit(1); 9 | } 10 | 11 | try { 12 | console.log(`正在更新所有项目到版本 ${newVersion}`); 13 | 14 | // 更新admin版本 15 | console.log('更新 admin 版本'); 16 | execSync(`cd admin && yarn version --new-version ${newVersion} --no-git-tag-version && cd ..`, { stdio: 'inherit' }); 17 | 18 | // 更新server版本 19 | console.log('更新 server 版本'); 20 | execSync(`cd server && yarn version --new-version ${newVersion} --no-git-tag-version && cd ..`, { stdio: 'inherit' }); 21 | 22 | // 更新blog版本 23 | console.log('更新 blog 版本'); 24 | execSync(`cd blog && yarn version --new-version ${newVersion} --no-git-tag-version && cd ..`, { stdio: 'inherit' }); 25 | 26 | console.log(`所有项目已更新到版本 ${newVersion}`); 27 | 28 | // 更新根目录的版本 29 | console.log('更新根目录版本'); 30 | execSync(`yarn version --new-version ${newVersion} --no-git-tag-version`, { stdio: 'inherit' }); 31 | 32 | console.log('所有项目版本更新完成'); 33 | } catch (error) { 34 | console.error('更新版本时发生错误:', error); 35 | process.exit(1); 36 | } --------------------------------------------------------------------------------