├── .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 |
2 |
3 |
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 |
2 |
3 | {{ option.title }}
4 | 选项已删除
5 |
6 |
7 |
31 |
38 |
--------------------------------------------------------------------------------
/admin/src/components/IpInfoDisplay.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | {{ ipInfo?.countryLong }}
4 |
5 | {{ ' ' + ipInfo?.region }}
6 |
7 |
8 | {{ ' ' + ipInfo?.city }}
9 |
10 |
11 |
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 += `
`
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 |

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 |
2 |
10 |
11 |
12 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/blog/components/AdsbygoogleHave.vue:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
45 |
--------------------------------------------------------------------------------
/blog/components/BangumiSeasonList.vue:
--------------------------------------------------------------------------------
1 |
2 |
19 |
20 |
25 |
26 |
--------------------------------------------------------------------------------
/blog/components/DivLoading.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 | {{ text }}
12 |
13 |
14 |
15 |
27 |
28 |
--------------------------------------------------------------------------------
/blog/components/Empty.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |

7 |
8 |
9 |
10 |
11 |
12 |
24 |
--------------------------------------------------------------------------------
/blog/components/GoTop.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
10 |
11 |
12 |
13 |
14 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/blog/components/LoadingDots.vue:
--------------------------------------------------------------------------------
1 |
2 | {{ dots }}
3 |
4 |
5 |
30 |
--------------------------------------------------------------------------------
/blog/components/PlayingGameList.vue:
--------------------------------------------------------------------------------
1 |
2 |
19 |
20 |
25 |
26 |
--------------------------------------------------------------------------------
/blog/components/PostAbout.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 | 相关博文:
8 |
9 |
10 |
26 |
27 |
28 |
29 |
51 |
52 |
--------------------------------------------------------------------------------
/blog/components/PostListFilterBtn.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 | 博
10 | 推
13 |
14 |
15 |
16 |
17 |
18 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/blog/components/PostShowHeaderListBtn.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 |
9 |
10 |
11 |
12 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/blog/components/PostVote.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 | 相关投票:
8 |
9 |
10 |
18 |
19 |
20 |
21 |
43 |
44 |
--------------------------------------------------------------------------------
/blog/components/Rating.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 | {{ rating }}分{{ ratingToText(rating) }}
9 |
10 |
14 | 暂无评分
15 |
16 |
17 |
18 |
26 |
38 |
--------------------------------------------------------------------------------
/blog/components/ReadingBookList.vue:
--------------------------------------------------------------------------------
1 |
2 |
19 |
20 |
25 |
26 |
--------------------------------------------------------------------------------
/blog/error.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
{{ error.statusCode }}
4 |
{{ error.message || error.statusMessage }}
5 |
6 |
返回首页
7 |
8 |
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 |
2 |
3 |
404
4 |
页面不见了?
5 |
6 |
返回首页
7 |
8 |
9 |
16 |
46 |
--------------------------------------------------------------------------------
/blog/pages/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/blog/pages/index/page/[id].vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
10 |
--------------------------------------------------------------------------------
/blog/pages/index/post/[id].vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
10 |
--------------------------------------------------------------------------------
/blog/pages/index/post/list.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/blog/pages/index/post/list/[[page]]/[[type]].vue:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
18 |
19 |
--------------------------------------------------------------------------------
/blog/pages/index/post/list/archive/[year]/[month].vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 | {{ title }}
8 |
9 |
10 |
11 |
12 |
42 |
--------------------------------------------------------------------------------
/blog/pages/index/post/list/archive/[year]/[month]/[page]/[[type]].vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
10 |
--------------------------------------------------------------------------------
/blog/pages/index/post/list/keyword/[keyword].vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 | 搜索:{{ keyword }}
7 |
8 |
9 |
10 |
11 |
40 |
--------------------------------------------------------------------------------
/blog/pages/index/post/list/keyword/[keyword]/[page]/[[type]].vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
10 |
--------------------------------------------------------------------------------
/blog/pages/index/post/list/sort/[sortid].vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 | 分类:{{ data.sortname }}
8 |
9 |
10 |
11 |
12 |
47 |
--------------------------------------------------------------------------------
/blog/pages/index/post/list/sort/[sortid]/[page]/[[type]].vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
10 |
--------------------------------------------------------------------------------
/blog/pages/index/post/list/tag/[tagid].vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 | 标签:#{{ data.data.tagname }}
8 |
9 |
10 |
11 |
12 |
43 |
--------------------------------------------------------------------------------
/blog/pages/index/post/list/tag/[tagid]/[page]/[[type]].vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
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 | }
--------------------------------------------------------------------------------