├── .gitattributes ├── .gitignore ├── .prettierrc ├── LICENSE ├── README.md ├── nest-cli.json ├── nodemon-debug.json ├── nodemon.json ├── package.json ├── pc ├── fonts │ ├── fontawesome │ │ ├── FontAwesome.otf │ │ ├── fontawesome-webfont.eot │ │ ├── fontawesome-webfont.svg │ │ ├── fontawesome-webfont.ttf │ │ ├── fontawesome-webfont.woff │ │ └── fontawesome-webfont.woff2 │ ├── iconfont.eot │ ├── iconfont.svg │ ├── iconfont.ttf │ └── ionicons │ │ ├── ionicons.svg │ │ ├── ionicons.ttf │ │ └── ionicons.woff ├── images │ ├── article │ │ ├── ad.png │ │ ├── app.png │ │ ├── icon_comment_no.png │ │ ├── like.png │ │ ├── social_collect.svg │ │ ├── social_collect_hover.svg │ │ ├── social_comment.svg │ │ ├── social_comment_hover.svg │ │ ├── social_qq.svg │ │ ├── social_qq2.svg │ │ ├── social_qq_hover.svg │ │ ├── social_wechat.svg │ │ ├── social_wechat_hover.svg │ │ ├── social_weibo.svg │ │ ├── social_weibo_hover.svg │ │ ├── social_zan.svg │ │ ├── social_zan_active.svg │ │ ├── social_zan_hover.svg │ │ └── zan.svg │ ├── avatar.jpg │ ├── avatar_default.png │ ├── avatar_default2.svg │ ├── boilingpoint │ │ ├── close.svg │ │ ├── empty.svg │ │ ├── expand.svg │ │ ├── expandover.svg │ │ ├── guideicon.svg │ │ ├── left.big.png │ │ ├── leftrotate.svg │ │ ├── leftrotateover.svg │ │ ├── loading.gif │ │ ├── notopic.svg │ │ ├── pinch.svg │ │ ├── pinchover.svg │ │ ├── right.big.png │ │ ├── rightrotate.svg │ │ ├── rightrotateover.svg │ │ ├── search-focus.svg │ │ └── search.svg │ ├── collection │ │ └── cover.png │ ├── cursor-left.png │ ├── cursor-right.png │ ├── editor │ │ ├── markdown.svg │ │ ├── rich.blockcode.png │ │ ├── rich.blockquote.png │ │ ├── rich.bold.png │ │ ├── rich.code.png │ │ ├── rich.h1.png │ │ ├── rich.h2.png │ │ ├── rich.image.png │ │ ├── rich.italic.png │ │ ├── rich.link.png │ │ ├── rich.ol.png │ │ ├── rich.ul.png │ │ ├── rich.underline.png │ │ ├── togglelayout.svg │ │ ├── togglelayout2.svg │ │ ├── uploadcover.svg │ │ ├── uploadcover2.svg │ │ └── uploadimg.svg │ ├── emojis │ │ ├── accept.png │ │ ├── airplane.png │ │ ├── alarm_clock.png │ │ ├── ambulance.png │ │ ├── angel.png │ │ ├── anger.png │ │ ├── angry.png │ │ ├── anguished.png │ │ ├── ant.png │ │ ├── apple.png │ │ ├── arrow_down.png │ │ ├── arrow_left.png │ │ ├── arrow_right.png │ │ ├── arrow_up.png │ │ ├── arrows_counterclockwise.png │ │ ├── atm.png │ │ ├── baby_chick.png │ │ ├── balloon.png │ │ ├── bangbang.png │ │ ├── bear.png │ │ ├── beer.png │ │ ├── beers.png │ │ ├── beetle.png │ │ ├── beginner.png │ │ ├── bell.png │ │ ├── bikini.png │ │ ├── birthday.png │ │ ├── black_nib.png │ │ ├── blossom.png │ │ ├── blush.png │ │ ├── bomb.png │ │ ├── boom.png │ │ ├── bow.png │ │ ├── bread.png │ │ ├── broken_heart.png │ │ ├── bug.png │ │ ├── bulb.png │ │ ├── cactus.png │ │ ├── cake.png │ │ ├── camera.png │ │ ├── candy.png │ │ ├── cat.png │ │ ├── chart.png │ │ ├── checkered_flag.png │ │ ├── cherries.png │ │ ├── cherry_blossom.png │ │ ├── chocolate_bar.png │ │ ├── christmas_tree.png │ │ ├── clap.png │ │ ├── clapper.png │ │ ├── closed_lock_with_key.png │ │ ├── closed_umbrella.png │ │ ├── cloud.png │ │ ├── clubs.png │ │ ├── cocktail.png │ │ ├── coffee.png │ │ ├── cold_sweat.png │ │ ├── confetti_ball.png │ │ ├── confounded.png │ │ ├── corn.png │ │ ├── cow.png │ │ ├── crocodile.png │ │ ├── crown.png │ │ ├── cry.png │ │ ├── crying_cat_face.png │ │ ├── crystal_ball.png │ │ ├── cupid.png │ │ ├── dancer.png │ │ ├── dancers.png │ │ ├── dart.png │ │ ├── dash.png │ │ ├── deciduous_tree.png │ │ ├── disappointed_relieved.png │ │ ├── dizzy_face.png │ │ ├── dog.png │ │ ├── dolphin.png │ │ ├── doughnut.png │ │ ├── dragon_face.png │ │ ├── dress.png │ │ ├── ear_of_rice.png │ │ ├── eight_spoked_asterisk.png │ │ ├── eyes.png │ │ ├── fallen_leaf.png │ │ ├── fearful.png │ │ ├── feet.png │ │ ├── first_quarter_moon.png │ │ ├── first_quarter_moon_with_face.png │ │ ├── fishing_pole_and_fish.png │ │ ├── fist.png │ │ ├── flushed.png │ │ ├── fries.png │ │ ├── frog.png │ │ ├── frowning.png │ │ ├── game_die.png │ │ ├── ghost.png │ │ ├── golf.png │ │ ├── grin.png │ │ ├── guitar.png │ │ ├── gun.png │ │ ├── hamster.png │ │ ├── hankey.png │ │ ├── hatched_chick.png │ │ ├── hatching_chick.png │ │ ├── heart.png │ │ ├── heart_eyes.png │ │ ├── heart_eyes_cat.png │ │ ├── heartbeat.png │ │ ├── heavy_check_mark.png │ │ ├── heavy_exclamation_mark.png │ │ ├── herb.png │ │ ├── hibiscus.png │ │ ├── high_brightness.png │ │ ├── high_heel.png │ │ ├── hocho.png │ │ ├── hushed.png │ │ ├── icecream.png │ │ ├── ideograph_advantage.png │ │ ├── innocent.png │ │ ├── interrobang.png │ │ ├── jack_o_lantern.png │ │ ├── jia1.png │ │ ├── jian1.png │ │ ├── joy.png │ │ ├── joy_cat.png │ │ ├── key.png │ │ ├── kiss.png │ │ ├── kissing_closed_eyes.png │ │ ├── kissing_heart.png │ │ ├── leaves.png │ │ ├── lipstick.png │ │ ├── lock.png │ │ ├── lollipop.png │ │ ├── mag.png │ │ ├── mag_right.png │ │ ├── mailbox_with_mail.png │ │ ├── mailbox_with_no_mail.png │ │ ├── man_with_gua_pi_mao.png │ │ ├── mask.png │ │ ├── metal.png │ │ ├── moneybag.png │ │ ├── monkey_face.png │ │ ├── moon.png │ │ ├── mushroom.png │ │ ├── musical_keyboard.png │ │ ├── no_bell.png │ │ ├── no_bicycles.png │ │ ├── no_mobile_phones.png │ │ ├── no_mouth.png │ │ ├── octocat.png │ │ ├── on.png │ │ ├── pensive.png │ │ ├── persevere.png │ │ ├── pray.png │ │ ├── rabbit.png │ │ ├── relaxed.png │ │ ├── relieved.png │ │ ├── ribbon.png │ │ ├── scream.png │ │ ├── scream_cat.png │ │ ├── skull.png │ │ ├── sleepy.png │ │ ├── smile.png │ │ ├── smile_cat.png │ │ ├── smiley.png │ │ ├── smiley_cat.png │ │ ├── smirk.png │ │ ├── smirk_cat.png │ │ ├── snowflake.png │ │ ├── snowman.png │ │ ├── sob.png │ │ ├── spaghetti.png │ │ ├── sparkles.png │ │ ├── sparkling_heart.png │ │ ├── squirrel.png │ │ ├── strawberry.png │ │ ├── stuck_out_tongue.png │ │ ├── stuck_out_tongue_closed_eyes.png │ │ ├── stuck_out_tongue_winking_eye.png │ │ ├── sunflower.png │ │ ├── sunglasses.png │ │ ├── sunny.png │ │ ├── sweat.png │ │ ├── sweat_drops.png │ │ ├── sweat_smile.png │ │ ├── tada.png │ │ ├── tiger.png │ │ ├── toilet.png │ │ ├── turtle.png │ │ ├── u7981.png │ │ ├── unamused.png │ │ ├── underage.png │ │ ├── up.png │ │ ├── v.png │ │ ├── watermelon.png │ │ ├── whale.png │ │ ├── whale2.png │ │ ├── wink.png │ │ ├── worried.png │ │ ├── yum.png │ │ └── zap.png │ ├── favicon.ico │ ├── handbook │ │ ├── buy-icon.svg │ │ ├── corner-mark.png │ │ ├── dir.svg │ │ ├── finished.svg │ │ ├── menu.svg │ │ ├── more.svg │ │ ├── next.svg │ │ ├── poster-cover.png │ │ ├── prev.svg │ │ ├── star.svg │ │ ├── star2.svg │ │ └── star_selected.svg │ ├── icon_comment_close.png │ ├── icp.png │ ├── index │ │ ├── book.svg │ │ ├── book1.jpg │ │ ├── book2.jpg │ │ ├── pin-icon.svg │ │ ├── qrcode.png │ │ ├── share-icon.svg │ │ └── write-icon.svg │ ├── logo.png │ ├── logo_backup.png │ ├── message │ │ └── post_nocontent.png │ ├── settings │ │ └── edit_icon.svg │ ├── social.png │ ├── test │ │ ├── admin.jpg │ │ └── crawler.png │ ├── user │ │ ├── emptybox.svg │ │ └── level │ │ │ └── lv1.svg │ └── zan.png ├── js │ ├── common │ │ ├── default.js │ │ ├── filters.js │ │ ├── net.js │ │ └── pinterest.js │ ├── components │ │ ├── TopNavSearch.vue │ │ ├── admin │ │ │ ├── book │ │ │ │ ├── CrawlContent.vue │ │ │ │ ├── CreateOrUpdateBookModal.vue │ │ │ │ └── CreateOrUpdateChapterModal.vue │ │ │ └── common │ │ │ │ ├── Header.vue │ │ │ │ ├── Sidebar.vue │ │ │ │ ├── SimpleUploader.vue │ │ │ │ └── User.vue │ │ ├── article │ │ │ ├── ArticleItem.vue │ │ │ ├── ArticleLoading.vue │ │ │ ├── ArticleShareQRCode.vue │ │ │ └── ArticleSocial.vue │ │ ├── boilingpoint │ │ │ ├── BoilingPointItem.vue │ │ │ ├── BoilingPointLoading.vue │ │ │ ├── BoilingPointModal.vue │ │ │ ├── BoilingPointTopicPopup.vue │ │ │ ├── ReportAlert.vue │ │ │ └── Share.vue │ │ ├── book │ │ │ ├── BookItem.vue │ │ │ └── BookLoading.vue │ │ ├── category │ │ │ ├── CategoryItem.vue │ │ │ └── CategoryLoading.vue │ │ ├── collection │ │ │ ├── collection.vue │ │ │ └── edit.vue │ │ ├── comment │ │ │ ├── CommentList.vue │ │ │ ├── ReplyIcon.vue │ │ │ └── ZanIcon.vue │ │ ├── common │ │ │ ├── Alert.vue │ │ │ ├── ErrorTip.vue │ │ │ ├── GlobalLoading.vue │ │ │ ├── More.vue │ │ │ ├── Paging.vue │ │ │ ├── Pinterest.vue │ │ │ ├── SuccessTip.vue │ │ │ ├── Uploader.vue │ │ │ ├── UploaderCroppieImage.vue │ │ │ ├── UploaderList.vue │ │ │ ├── UploaderPure.vue │ │ │ ├── UserDropdown.vue │ │ │ └── sidemenu │ │ │ │ ├── CollapsedMenu.vue │ │ │ │ ├── CommonIcon.vue │ │ │ │ ├── Icons.vue │ │ │ │ ├── SideMenu.vue │ │ │ │ ├── SideMenuItem.vue │ │ │ │ ├── index.js │ │ │ │ ├── itemmixin.js │ │ │ │ ├── mixin.js │ │ │ │ ├── tools.js │ │ │ │ └── util.js │ │ ├── editor │ │ │ ├── BoilingPointEditor.vue │ │ │ ├── CommentRichEditor.vue │ │ │ ├── CommentRichEditorEmoji.vue │ │ │ ├── CommentRichEditorEmojiIcons.vue │ │ │ ├── EditorHeader.vue │ │ │ ├── MarkdownEditor.vue │ │ │ ├── MarkdownEditorMenubar.vue │ │ │ ├── MyHardBreak.js │ │ │ ├── RichEditor.vue │ │ │ ├── RichEditorMenubar.vue │ │ │ └── tui-editor-ext.js │ │ ├── handbook │ │ │ ├── AddChapterAlert.vue │ │ │ ├── AgreementAlert.vue │ │ │ ├── HandBookComment.vue │ │ │ ├── HandBookHeader.vue │ │ │ ├── HandBookItem.vue │ │ │ ├── MyHandBookItem.vue │ │ │ ├── PriceAlert.vue │ │ │ ├── StarModal.vue │ │ │ └── UserList.vue │ │ ├── messages │ │ │ ├── Chat.vue │ │ │ ├── Comment.vue │ │ │ ├── Follow.vue │ │ │ ├── Like.vue │ │ │ ├── Money.vue │ │ │ ├── Other.vue │ │ │ ├── PostArticle.vue │ │ │ └── Side.vue │ │ ├── tag │ │ │ ├── TagInfo.vue │ │ │ └── TagItem.vue │ │ └── user │ │ │ ├── FollowBtn.vue │ │ │ ├── UserBusinessCard.vue │ │ │ ├── UserItem.vue │ │ │ └── UserLoading.vue │ ├── constants │ │ ├── article.js │ │ ├── book.js │ │ ├── entity.js │ │ ├── error.js │ │ └── exercise.js │ ├── entry │ │ ├── admin │ │ │ └── app.js │ │ ├── article │ │ │ └── articleDetail.js │ │ ├── boilingpoint │ │ │ ├── boilingpoint.js │ │ │ └── boilingpointDetail.js │ │ ├── book │ │ │ ├── bookDetail.js │ │ │ ├── books.js │ │ │ └── chapter.js │ │ ├── codeStats.js │ │ ├── collection │ │ │ ├── collection.js │ │ │ └── edit.js │ │ ├── editor │ │ │ ├── drafts.js │ │ │ ├── editMarkdownDraft.js │ │ │ ├── editRichDraft.js │ │ │ ├── editor.md.js │ │ │ └── published.js │ │ ├── handbook │ │ │ ├── editHandbook.js │ │ │ ├── handbookDetail.js │ │ │ └── handbooks.js │ │ ├── index.js │ │ ├── messages.js │ │ ├── recommendations │ │ │ └── authors.js │ │ ├── search │ │ │ └── search.js │ │ ├── settings │ │ │ └── settings.js │ │ ├── signin.js │ │ ├── signup.js │ │ ├── tag │ │ │ ├── tag.js │ │ │ └── tagDetail.js │ │ └── user │ │ │ └── user.js │ ├── libs │ │ ├── croppie.min.js │ │ ├── geetest.js │ │ ├── highlight.min.js │ │ ├── jqueryx-1.11.3.min.js │ │ ├── qrcode.min.js │ │ └── simplemde.min.js │ ├── pages │ │ ├── CodeStats.vue │ │ ├── Index.vue │ │ ├── Messages.vue │ │ ├── SigninApp.vue │ │ ├── SignupApp.vue │ │ ├── admin │ │ │ ├── 404.vue │ │ │ ├── article │ │ │ │ ├── CategoryList.vue │ │ │ │ ├── Crawler.vue │ │ │ │ ├── CrawlerList.vue │ │ │ │ └── TagList.vue │ │ │ ├── boilingpoint │ │ │ │ └── Topic.vue │ │ │ ├── book │ │ │ │ ├── BookCategoryList.vue │ │ │ │ ├── BookList.vue │ │ │ │ └── EditBook.vue │ │ │ ├── exercise │ │ │ │ ├── EditQuestion.vue │ │ │ │ ├── ExerciseList.vue │ │ │ │ └── QuestionList.vue │ │ │ ├── index.vue │ │ │ ├── layout.vue │ │ │ └── test.vue │ │ ├── boilingpoint │ │ │ └── BoilingPoint.vue │ │ ├── book │ │ │ ├── BookDetail.vue │ │ │ ├── Books.vue │ │ │ ├── Chapter.vue │ │ │ └── ChapterLayout.vue │ │ ├── editor │ │ │ ├── Drafts.vue │ │ │ ├── EditMarkdownDraft.vue │ │ │ ├── EditRichDraft.vue │ │ │ └── Published.vue │ │ ├── handbook │ │ │ ├── CreateHandBookView.vue │ │ │ ├── EditHandBookLayout.vue │ │ │ └── EditHandBookView.vue │ │ ├── recommendations │ │ │ ├── AuthorView.vue │ │ │ └── Layout.vue │ │ ├── search │ │ │ ├── All.vue │ │ │ ├── Article.vue │ │ │ ├── Category.vue │ │ │ └── User.vue │ │ ├── settings │ │ │ ├── Layout.vue │ │ │ ├── PasswordView.vue │ │ │ └── ProfileView.vue │ │ ├── tag │ │ │ ├── Tag.vue │ │ │ └── TagDetail.vue │ │ └── user │ │ │ ├── ArticleView.vue │ │ │ ├── BoilingPointView.vue │ │ │ ├── BuyHandBookView.vue │ │ │ ├── CollectionView.vue │ │ │ ├── FollowTagView.vue │ │ │ ├── FollowView.vue │ │ │ ├── FollowerView.vue │ │ │ ├── Layout.vue │ │ │ ├── LikeArticleView.vue │ │ │ ├── LikeBoilingPointView.vue │ │ │ └── WriteHandBookView.vue │ └── utils │ │ ├── date.js │ │ ├── dom.js │ │ ├── event.js │ │ ├── tree.js │ │ ├── utils.js │ │ └── vue.js ├── package.json ├── styles │ ├── admin.scss │ ├── admin │ │ └── book │ │ │ └── edit_book.scss │ ├── article │ │ ├── articleDetail.scss │ │ └── articleDisplay.scss │ ├── boilingpoint │ │ ├── boilingpoint.scss │ │ └── boilingpointDetail.scss │ ├── book │ │ ├── bookDetail.scss │ │ ├── books.scss │ │ └── chapter.scss │ ├── codeStats.scss │ ├── collection │ │ ├── collection.css │ │ └── new.css │ ├── comment │ │ ├── commentDisplay.scss │ │ └── comments.scss │ ├── editor │ │ ├── drafts.scss │ │ ├── editDraft.scss │ │ ├── md.editor.frame.scss │ │ └── rich.editor.scss │ ├── handbook │ │ ├── editHandbook.scss │ │ ├── handbookDetail.scss │ │ └── handbooks.scss │ ├── iconfont.scss │ ├── index.css │ ├── libs │ │ ├── animate.css │ │ ├── croppie.min.css │ │ ├── font-awesome.css │ │ ├── highlight.codestyle.css │ │ ├── highlight.min.css │ │ ├── hint.css │ │ ├── iview-3.3.3.min.css │ │ └── simplemde.min.css │ ├── main.scss │ ├── messages.scss │ ├── reset.scss │ ├── search │ │ ├── search.scss │ │ └── search2.scss │ ├── settings │ │ └── settings.scss │ ├── signin.css │ ├── signup.css │ ├── tag │ │ └── tagDetail.scss │ ├── user │ │ └── user.scss │ └── variable.scss ├── webpack.common.js ├── webpack.dev.js └── webpack.prod.js ├── src ├── admin │ ├── admin.module.ts │ ├── article.controller.ts │ └── index.controller.ts ├── app.module.ts ├── boilingpoint │ ├── boilingpoint.controller.ts │ ├── boilingpoint.module.ts │ ├── boilingpoint.service.ts │ ├── dto │ │ ├── edit-boilingpoint.dto.ts │ │ └── edit-topic.dto.ts │ ├── topic.admin.controller.ts │ ├── topic.controller.ts │ └── topic.service.ts ├── book │ ├── book.controller.ts │ ├── book.module.ts │ ├── book.service.ts │ ├── dto │ │ ├── create-book-chapter.dto.ts │ │ ├── create-book-star.dto.ts │ │ ├── create-book.dto.ts │ │ ├── create-bookcategory.dto.ts │ │ ├── create-handbook.dto.ts │ │ ├── update-book-chapter.dto.ts │ │ ├── update-book.dto.ts │ │ ├── update-category.dto.ts │ │ ├── update-handbook-chapter.dto.ts │ │ └── update-handbook.dto.ts │ ├── handbook.controller.ts │ └── handbook.service.ts ├── bootstrap.ts ├── cms │ ├── article.controller.ts │ ├── article.service.ts │ ├── category.controller.ts │ ├── category.service.ts │ ├── cms.module.ts │ ├── collection.controller.ts │ ├── collection.service.ts │ ├── comment.controller.ts │ ├── comment.service.ts │ ├── crawler.admin.controller.ts │ ├── crawler.service.ts │ ├── draft.service.ts │ ├── dto │ │ ├── crawler-article.dto.ts │ │ ├── crawler.dto.ts │ │ ├── create-category.dto.ts │ │ ├── create-collection.dto.ts │ │ ├── create-comment.dto.ts │ │ ├── create-draft.dto.ts │ │ ├── create-tag.dto.ts │ │ ├── edit-article.dto.ts │ │ ├── switch-editor.dto.ts │ │ ├── update-category.dto.ts │ │ └── update-tag.dto.ts │ ├── editor.controller.ts │ ├── index.controller.ts │ ├── message.controller.ts │ ├── message.service.ts │ ├── recommend.controller.ts │ ├── search.controller.ts │ ├── search.service.ts │ ├── tag.controller.ts │ ├── tag.service.ts │ ├── uc.controller.ts │ └── upload.service.ts ├── common │ ├── common.controller.ts │ ├── common.module.ts │ ├── logger.service.ts │ └── oss.service.ts ├── config │ ├── cfg.default.ts │ ├── cfg.development.ts │ ├── cfg.production.ts │ ├── cfg.test.ts │ ├── config.module.ts │ ├── config.service.ts │ └── type │ │ ├── AliyunOSSConfig.ts │ │ ├── AliyunSMSConfig.ts │ │ ├── BaseConfig.ts │ │ ├── DBConfig.ts │ │ ├── GeetestCaptcha.ts │ │ ├── GithubConfig.ts │ │ ├── RedisConfig.ts │ │ ├── ServerConfig.ts │ │ ├── StaticConfig.ts │ │ ├── StatsDConfig.ts │ │ └── WeiboConfig.ts ├── constants │ ├── article.ts │ ├── boilingpoint.ts │ ├── book.ts │ ├── category.ts │ ├── comment.ts │ ├── constants.ts │ ├── error.ts │ ├── exercise.ts │ ├── handbook.ts │ ├── social.ts │ └── tag.ts ├── core │ ├── decorators │ │ ├── roles.decorator.ts │ │ └── user.decorator.ts │ ├── exception │ │ └── my-http.exception.ts │ ├── filters │ │ └── global-exceptoin.filter.ts │ ├── guards │ │ ├── active.guard.ts │ │ └── roles.guard.ts │ ├── interceptors │ │ └── transform-res.interceptor.ts │ ├── middleware │ │ ├── compression.middleware.ts │ │ ├── cookie-parser.middleware.ts │ │ ├── cors.middleware.ts │ │ ├── csrf.middleware.ts │ │ ├── helmet.middleware.ts │ │ ├── ip.middleware.ts │ │ ├── locals.middleware.ts │ │ ├── rate-limit.middleware.ts │ │ └── user.middleware.ts │ ├── pipes │ │ ├── must-int.pipe.ts │ │ ├── parse-page.pipe.ts │ │ ├── should-int.pipe.ts │ │ └── validate-dto.pipe.ts │ └── types │ │ └── net.ts ├── cron │ ├── user_article_count.ts │ ├── user_follower_count.ts │ └── user_liked_count.ts ├── dbscript │ ├── addColumn.ts │ ├── comment.ts │ ├── commentRoot.ts │ ├── init_db.ts │ ├── mdToHTML.ts │ ├── query.ts │ ├── renameTable.ts │ ├── run.ts │ ├── s_book_chapters.ts │ ├── tables.ts │ ├── updateRootCommentCount.ts │ ├── updateUserArticleCount.ts │ ├── updateUserArticleViewCount.ts │ ├── updateUserLikedCount.ts │ └── user.ts ├── entity │ ├── article.entity.ts │ ├── boilingpoint.entity.ts │ ├── book.entity.ts │ ├── category.entity.ts │ ├── collection.entity.ts │ ├── comment.entity.ts │ ├── crawler.entity.ts │ ├── draft.entity.ts │ ├── handbook.category.entity.ts │ ├── handbook.entity.ts │ ├── image.entity.ts │ ├── listresult.entity.ts │ ├── postmsg.entity.ts │ ├── question.entity.ts │ ├── question.option.entity.ts │ ├── settings.entity.ts │ ├── stats.entity.ts │ ├── tag.entity.ts │ ├── timeline.entity.ts │ └── user.entity.ts ├── exercise │ ├── dto │ │ ├── edit-question.dto.ts │ │ └── validation │ │ │ ├── AnswersValidator.ts │ │ │ └── OptionsValidator.ts │ ├── exercise.module.ts │ ├── question.controller.ts │ └── question.service.ts ├── main.hmr.ts ├── main.ts ├── redis │ ├── redis.module.ts │ └── redis.service.ts ├── stats │ ├── codestats.controller.ts │ ├── stats.controller.ts │ ├── stats.module.ts │ └── stats.service.ts ├── user │ ├── admin.controller.ts │ ├── dto │ │ ├── signin.dto.ts │ │ ├── signup.dto.ts │ │ ├── sms.dto.ts │ │ ├── update-password.dto.ts │ │ ├── update-userinfo.dto.ts │ │ └── update-userstatus.dto.ts │ ├── user.controller.ts │ ├── user.module.ts │ └── user.service.ts └── utils │ ├── common.ts │ ├── query.ts │ ├── security.ts │ ├── social.ts │ └── viewfilter.ts ├── test ├── e2e │ ├── article.e2e-spec.ts │ └── user.e2e-spec.ts └── jest-e2e.json ├── tsconfig.build.json ├── tsconfig.json ├── tslint.json └── views ├── component ├── article │ └── articles.njk ├── book │ └── bookList.njk ├── cms │ ├── pagination.njk │ ├── search │ │ └── sidebar.njk │ └── timeline.njk ├── footer.njk ├── handbook │ └── handbookList.njk ├── header.njk ├── search │ └── searchtype.njk ├── sidetool.njk └── user │ └── recommendusers.njk ├── layout ├── admin.njk ├── default.njk └── empty.njk ├── macros └── js.njk └── pages ├── admin └── app.njk ├── article └── articleDetail.njk ├── boilingpoint ├── boilingpoint.njk └── boilingpointDetail.njk ├── books ├── bookDetail.njk ├── books.njk └── chapter.njk ├── codeStats.njk ├── collection ├── collection.njk └── edit.njk ├── editor ├── drafts.njk ├── editMarkdownArticle.njk ├── editRichArticle.njk ├── editor.md.njk └── published.njk ├── handbook ├── editHandbook.njk ├── handbookDetail.njk └── handbooks.njk ├── index.njk ├── messages.njk ├── recommendations └── authors.njk ├── search └── search.njk ├── settings └── settings.njk ├── signin.njk ├── signup.njk ├── tag ├── tag.njk └── tagDetail.njk └── user └── user.njk /.gitattributes: -------------------------------------------------------------------------------- 1 | *.js linguist-language=javascript 2 | *.css linguist-language=javascript 3 | *.html linguist-language=javascript 4 | *.vue linguist-language=javascript -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # dependencies 2 | node_modules/ 3 | 4 | # IDE 5 | /.idea 6 | /.awcache 7 | .vscode 8 | 9 | # Log files 10 | npm-debug.log* 11 | 12 | # OS 13 | .DS_Store 14 | 15 | # Tests 16 | /coverage 17 | /.nyc_output 18 | 19 | dist 20 | package-lock.json 21 | 22 | ossconfig 23 | publish.sh 24 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "trailingComma": "all" 4 | } -------------------------------------------------------------------------------- /nest-cli.json: -------------------------------------------------------------------------------- 1 | { 2 | "language": "ts", 3 | "collection": "@nestjs/schematics", 4 | "sourceRoot": "src" 5 | } 6 | -------------------------------------------------------------------------------- /nodemon-debug.json: -------------------------------------------------------------------------------- 1 | { 2 | "watch": ["src"], 3 | "ext": "ts", 4 | "ignore": ["src/**/*.spec.ts"], 5 | "exec": "node --inspect-brk -r ts-node/register -r tsconfig-paths/register src/main.ts" 6 | } 7 | -------------------------------------------------------------------------------- /nodemon.json: -------------------------------------------------------------------------------- 1 | { 2 | "watch": ["dist"], 3 | "ext": "js", 4 | "exec": "node dist/main" 5 | } 6 | -------------------------------------------------------------------------------- /pc/fonts/fontawesome/FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/fonts/fontawesome/FontAwesome.otf -------------------------------------------------------------------------------- /pc/fonts/fontawesome/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/fonts/fontawesome/fontawesome-webfont.eot -------------------------------------------------------------------------------- /pc/fonts/fontawesome/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/fonts/fontawesome/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /pc/fonts/fontawesome/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/fonts/fontawesome/fontawesome-webfont.woff -------------------------------------------------------------------------------- /pc/fonts/fontawesome/fontawesome-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/fonts/fontawesome/fontawesome-webfont.woff2 -------------------------------------------------------------------------------- /pc/fonts/iconfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/fonts/iconfont.eot -------------------------------------------------------------------------------- /pc/fonts/iconfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/fonts/iconfont.ttf -------------------------------------------------------------------------------- /pc/fonts/ionicons/ionicons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/fonts/ionicons/ionicons.ttf -------------------------------------------------------------------------------- /pc/fonts/ionicons/ionicons.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/fonts/ionicons/ionicons.woff -------------------------------------------------------------------------------- /pc/images/article/ad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/article/ad.png -------------------------------------------------------------------------------- /pc/images/article/app.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/article/app.png -------------------------------------------------------------------------------- /pc/images/article/icon_comment_no.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/article/icon_comment_no.png -------------------------------------------------------------------------------- /pc/images/article/like.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/article/like.png -------------------------------------------------------------------------------- /pc/images/article/social_collect.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /pc/images/article/social_collect_hover.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /pc/images/article/social_comment.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /pc/images/article/social_comment_hover.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /pc/images/article/social_qq.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /pc/images/article/social_qq2.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /pc/images/article/social_qq_hover.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /pc/images/article/social_zan.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /pc/images/article/social_zan_active.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /pc/images/article/social_zan_hover.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /pc/images/article/zan.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /pc/images/avatar.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/avatar.jpg -------------------------------------------------------------------------------- /pc/images/avatar_default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/avatar_default.png -------------------------------------------------------------------------------- /pc/images/boilingpoint/close.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /pc/images/boilingpoint/expand.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /pc/images/boilingpoint/expandover.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /pc/images/boilingpoint/guideicon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /pc/images/boilingpoint/left.big.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/boilingpoint/left.big.png -------------------------------------------------------------------------------- /pc/images/boilingpoint/leftrotate.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /pc/images/boilingpoint/leftrotateover.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /pc/images/boilingpoint/loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/boilingpoint/loading.gif -------------------------------------------------------------------------------- /pc/images/boilingpoint/notopic.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /pc/images/boilingpoint/pinch.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /pc/images/boilingpoint/pinchover.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /pc/images/boilingpoint/right.big.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/boilingpoint/right.big.png -------------------------------------------------------------------------------- /pc/images/boilingpoint/rightrotate.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /pc/images/boilingpoint/rightrotateover.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /pc/images/boilingpoint/search.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /pc/images/collection/cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/collection/cover.png -------------------------------------------------------------------------------- /pc/images/cursor-left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/cursor-left.png -------------------------------------------------------------------------------- /pc/images/cursor-right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/cursor-right.png -------------------------------------------------------------------------------- /pc/images/editor/rich.blockcode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/editor/rich.blockcode.png -------------------------------------------------------------------------------- /pc/images/editor/rich.blockquote.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/editor/rich.blockquote.png -------------------------------------------------------------------------------- /pc/images/editor/rich.bold.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/editor/rich.bold.png -------------------------------------------------------------------------------- /pc/images/editor/rich.code.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/editor/rich.code.png -------------------------------------------------------------------------------- /pc/images/editor/rich.h1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/editor/rich.h1.png -------------------------------------------------------------------------------- /pc/images/editor/rich.h2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/editor/rich.h2.png -------------------------------------------------------------------------------- /pc/images/editor/rich.image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/editor/rich.image.png -------------------------------------------------------------------------------- /pc/images/editor/rich.italic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/editor/rich.italic.png -------------------------------------------------------------------------------- /pc/images/editor/rich.link.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/editor/rich.link.png -------------------------------------------------------------------------------- /pc/images/editor/rich.ol.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/editor/rich.ol.png -------------------------------------------------------------------------------- /pc/images/editor/rich.ul.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/editor/rich.ul.png -------------------------------------------------------------------------------- /pc/images/editor/rich.underline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/editor/rich.underline.png -------------------------------------------------------------------------------- /pc/images/editor/uploadcover.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 699ED11E-7F16-40A5-89DD-C9ADE30CCB4C 5 | Created with sketchtool. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /pc/images/editor/uploadcover2.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 699ED11E-7F16-40A5-89DD-C9ADE30CCB4C 5 | Created with sketchtool. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /pc/images/emojis/accept.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/accept.png -------------------------------------------------------------------------------- /pc/images/emojis/airplane.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/airplane.png -------------------------------------------------------------------------------- /pc/images/emojis/alarm_clock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/alarm_clock.png -------------------------------------------------------------------------------- /pc/images/emojis/ambulance.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/ambulance.png -------------------------------------------------------------------------------- /pc/images/emojis/angel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/angel.png -------------------------------------------------------------------------------- /pc/images/emojis/anger.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/anger.png -------------------------------------------------------------------------------- /pc/images/emojis/angry.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/angry.png -------------------------------------------------------------------------------- /pc/images/emojis/anguished.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/anguished.png -------------------------------------------------------------------------------- /pc/images/emojis/ant.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/ant.png -------------------------------------------------------------------------------- /pc/images/emojis/apple.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/apple.png -------------------------------------------------------------------------------- /pc/images/emojis/arrow_down.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/arrow_down.png -------------------------------------------------------------------------------- /pc/images/emojis/arrow_left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/arrow_left.png -------------------------------------------------------------------------------- /pc/images/emojis/arrow_right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/arrow_right.png -------------------------------------------------------------------------------- /pc/images/emojis/arrow_up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/arrow_up.png -------------------------------------------------------------------------------- /pc/images/emojis/arrows_counterclockwise.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/arrows_counterclockwise.png -------------------------------------------------------------------------------- /pc/images/emojis/atm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/atm.png -------------------------------------------------------------------------------- /pc/images/emojis/baby_chick.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/baby_chick.png -------------------------------------------------------------------------------- /pc/images/emojis/balloon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/balloon.png -------------------------------------------------------------------------------- /pc/images/emojis/bangbang.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/bangbang.png -------------------------------------------------------------------------------- /pc/images/emojis/bear.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/bear.png -------------------------------------------------------------------------------- /pc/images/emojis/beer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/beer.png -------------------------------------------------------------------------------- /pc/images/emojis/beers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/beers.png -------------------------------------------------------------------------------- /pc/images/emojis/beetle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/beetle.png -------------------------------------------------------------------------------- /pc/images/emojis/beginner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/beginner.png -------------------------------------------------------------------------------- /pc/images/emojis/bell.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/bell.png -------------------------------------------------------------------------------- /pc/images/emojis/bikini.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/bikini.png -------------------------------------------------------------------------------- /pc/images/emojis/birthday.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/birthday.png -------------------------------------------------------------------------------- /pc/images/emojis/black_nib.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/black_nib.png -------------------------------------------------------------------------------- /pc/images/emojis/blossom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/blossom.png -------------------------------------------------------------------------------- /pc/images/emojis/blush.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/blush.png -------------------------------------------------------------------------------- /pc/images/emojis/bomb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/bomb.png -------------------------------------------------------------------------------- /pc/images/emojis/boom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/boom.png -------------------------------------------------------------------------------- /pc/images/emojis/bow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/bow.png -------------------------------------------------------------------------------- /pc/images/emojis/bread.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/bread.png -------------------------------------------------------------------------------- /pc/images/emojis/broken_heart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/broken_heart.png -------------------------------------------------------------------------------- /pc/images/emojis/bug.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/bug.png -------------------------------------------------------------------------------- /pc/images/emojis/bulb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/bulb.png -------------------------------------------------------------------------------- /pc/images/emojis/cactus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/cactus.png -------------------------------------------------------------------------------- /pc/images/emojis/cake.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/cake.png -------------------------------------------------------------------------------- /pc/images/emojis/camera.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/camera.png -------------------------------------------------------------------------------- /pc/images/emojis/candy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/candy.png -------------------------------------------------------------------------------- /pc/images/emojis/cat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/cat.png -------------------------------------------------------------------------------- /pc/images/emojis/chart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/chart.png -------------------------------------------------------------------------------- /pc/images/emojis/checkered_flag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/checkered_flag.png -------------------------------------------------------------------------------- /pc/images/emojis/cherries.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/cherries.png -------------------------------------------------------------------------------- /pc/images/emojis/cherry_blossom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/cherry_blossom.png -------------------------------------------------------------------------------- /pc/images/emojis/chocolate_bar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/chocolate_bar.png -------------------------------------------------------------------------------- /pc/images/emojis/christmas_tree.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/christmas_tree.png -------------------------------------------------------------------------------- /pc/images/emojis/clap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/clap.png -------------------------------------------------------------------------------- /pc/images/emojis/clapper.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/clapper.png -------------------------------------------------------------------------------- /pc/images/emojis/closed_lock_with_key.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/closed_lock_with_key.png -------------------------------------------------------------------------------- /pc/images/emojis/closed_umbrella.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/closed_umbrella.png -------------------------------------------------------------------------------- /pc/images/emojis/cloud.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/cloud.png -------------------------------------------------------------------------------- /pc/images/emojis/clubs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/clubs.png -------------------------------------------------------------------------------- /pc/images/emojis/cocktail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/cocktail.png -------------------------------------------------------------------------------- /pc/images/emojis/coffee.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/coffee.png -------------------------------------------------------------------------------- /pc/images/emojis/cold_sweat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/cold_sweat.png -------------------------------------------------------------------------------- /pc/images/emojis/confetti_ball.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/confetti_ball.png -------------------------------------------------------------------------------- /pc/images/emojis/confounded.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/confounded.png -------------------------------------------------------------------------------- /pc/images/emojis/corn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/corn.png -------------------------------------------------------------------------------- /pc/images/emojis/cow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/cow.png -------------------------------------------------------------------------------- /pc/images/emojis/crocodile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/crocodile.png -------------------------------------------------------------------------------- /pc/images/emojis/crown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/crown.png -------------------------------------------------------------------------------- /pc/images/emojis/cry.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/cry.png -------------------------------------------------------------------------------- /pc/images/emojis/crying_cat_face.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/crying_cat_face.png -------------------------------------------------------------------------------- /pc/images/emojis/crystal_ball.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/crystal_ball.png -------------------------------------------------------------------------------- /pc/images/emojis/cupid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/cupid.png -------------------------------------------------------------------------------- /pc/images/emojis/dancer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/dancer.png -------------------------------------------------------------------------------- /pc/images/emojis/dancers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/dancers.png -------------------------------------------------------------------------------- /pc/images/emojis/dart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/dart.png -------------------------------------------------------------------------------- /pc/images/emojis/dash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/dash.png -------------------------------------------------------------------------------- /pc/images/emojis/deciduous_tree.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/deciduous_tree.png -------------------------------------------------------------------------------- /pc/images/emojis/disappointed_relieved.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/disappointed_relieved.png -------------------------------------------------------------------------------- /pc/images/emojis/dizzy_face.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/dizzy_face.png -------------------------------------------------------------------------------- /pc/images/emojis/dog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/dog.png -------------------------------------------------------------------------------- /pc/images/emojis/dolphin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/dolphin.png -------------------------------------------------------------------------------- /pc/images/emojis/doughnut.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/doughnut.png -------------------------------------------------------------------------------- /pc/images/emojis/dragon_face.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/dragon_face.png -------------------------------------------------------------------------------- /pc/images/emojis/dress.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/dress.png -------------------------------------------------------------------------------- /pc/images/emojis/ear_of_rice.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/ear_of_rice.png -------------------------------------------------------------------------------- /pc/images/emojis/eight_spoked_asterisk.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/eight_spoked_asterisk.png -------------------------------------------------------------------------------- /pc/images/emojis/eyes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/eyes.png -------------------------------------------------------------------------------- /pc/images/emojis/fallen_leaf.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/fallen_leaf.png -------------------------------------------------------------------------------- /pc/images/emojis/fearful.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/fearful.png -------------------------------------------------------------------------------- /pc/images/emojis/feet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/feet.png -------------------------------------------------------------------------------- /pc/images/emojis/first_quarter_moon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/first_quarter_moon.png -------------------------------------------------------------------------------- /pc/images/emojis/first_quarter_moon_with_face.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/first_quarter_moon_with_face.png -------------------------------------------------------------------------------- /pc/images/emojis/fishing_pole_and_fish.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/fishing_pole_and_fish.png -------------------------------------------------------------------------------- /pc/images/emojis/fist.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/fist.png -------------------------------------------------------------------------------- /pc/images/emojis/flushed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/flushed.png -------------------------------------------------------------------------------- /pc/images/emojis/fries.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/fries.png -------------------------------------------------------------------------------- /pc/images/emojis/frog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/frog.png -------------------------------------------------------------------------------- /pc/images/emojis/frowning.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/frowning.png -------------------------------------------------------------------------------- /pc/images/emojis/game_die.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/game_die.png -------------------------------------------------------------------------------- /pc/images/emojis/ghost.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/ghost.png -------------------------------------------------------------------------------- /pc/images/emojis/golf.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/golf.png -------------------------------------------------------------------------------- /pc/images/emojis/grin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/grin.png -------------------------------------------------------------------------------- /pc/images/emojis/guitar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/guitar.png -------------------------------------------------------------------------------- /pc/images/emojis/gun.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/gun.png -------------------------------------------------------------------------------- /pc/images/emojis/hamster.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/hamster.png -------------------------------------------------------------------------------- /pc/images/emojis/hankey.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/hankey.png -------------------------------------------------------------------------------- /pc/images/emojis/hatched_chick.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/hatched_chick.png -------------------------------------------------------------------------------- /pc/images/emojis/hatching_chick.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/hatching_chick.png -------------------------------------------------------------------------------- /pc/images/emojis/heart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/heart.png -------------------------------------------------------------------------------- /pc/images/emojis/heart_eyes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/heart_eyes.png -------------------------------------------------------------------------------- /pc/images/emojis/heart_eyes_cat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/heart_eyes_cat.png -------------------------------------------------------------------------------- /pc/images/emojis/heartbeat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/heartbeat.png -------------------------------------------------------------------------------- /pc/images/emojis/heavy_check_mark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/heavy_check_mark.png -------------------------------------------------------------------------------- /pc/images/emojis/heavy_exclamation_mark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/heavy_exclamation_mark.png -------------------------------------------------------------------------------- /pc/images/emojis/herb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/herb.png -------------------------------------------------------------------------------- /pc/images/emojis/hibiscus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/hibiscus.png -------------------------------------------------------------------------------- /pc/images/emojis/high_brightness.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/high_brightness.png -------------------------------------------------------------------------------- /pc/images/emojis/high_heel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/high_heel.png -------------------------------------------------------------------------------- /pc/images/emojis/hocho.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/hocho.png -------------------------------------------------------------------------------- /pc/images/emojis/hushed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/hushed.png -------------------------------------------------------------------------------- /pc/images/emojis/icecream.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/icecream.png -------------------------------------------------------------------------------- /pc/images/emojis/ideograph_advantage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/ideograph_advantage.png -------------------------------------------------------------------------------- /pc/images/emojis/innocent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/innocent.png -------------------------------------------------------------------------------- /pc/images/emojis/interrobang.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/interrobang.png -------------------------------------------------------------------------------- /pc/images/emojis/jack_o_lantern.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/jack_o_lantern.png -------------------------------------------------------------------------------- /pc/images/emojis/jia1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/jia1.png -------------------------------------------------------------------------------- /pc/images/emojis/jian1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/jian1.png -------------------------------------------------------------------------------- /pc/images/emojis/joy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/joy.png -------------------------------------------------------------------------------- /pc/images/emojis/joy_cat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/joy_cat.png -------------------------------------------------------------------------------- /pc/images/emojis/key.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/key.png -------------------------------------------------------------------------------- /pc/images/emojis/kiss.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/kiss.png -------------------------------------------------------------------------------- /pc/images/emojis/kissing_closed_eyes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/kissing_closed_eyes.png -------------------------------------------------------------------------------- /pc/images/emojis/kissing_heart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/kissing_heart.png -------------------------------------------------------------------------------- /pc/images/emojis/leaves.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/leaves.png -------------------------------------------------------------------------------- /pc/images/emojis/lipstick.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/lipstick.png -------------------------------------------------------------------------------- /pc/images/emojis/lock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/lock.png -------------------------------------------------------------------------------- /pc/images/emojis/lollipop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/lollipop.png -------------------------------------------------------------------------------- /pc/images/emojis/mag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/mag.png -------------------------------------------------------------------------------- /pc/images/emojis/mag_right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/mag_right.png -------------------------------------------------------------------------------- /pc/images/emojis/mailbox_with_mail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/mailbox_with_mail.png -------------------------------------------------------------------------------- /pc/images/emojis/mailbox_with_no_mail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/mailbox_with_no_mail.png -------------------------------------------------------------------------------- /pc/images/emojis/man_with_gua_pi_mao.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/man_with_gua_pi_mao.png -------------------------------------------------------------------------------- /pc/images/emojis/mask.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/mask.png -------------------------------------------------------------------------------- /pc/images/emojis/metal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/metal.png -------------------------------------------------------------------------------- /pc/images/emojis/moneybag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/moneybag.png -------------------------------------------------------------------------------- /pc/images/emojis/monkey_face.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/monkey_face.png -------------------------------------------------------------------------------- /pc/images/emojis/moon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/moon.png -------------------------------------------------------------------------------- /pc/images/emojis/mushroom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/mushroom.png -------------------------------------------------------------------------------- /pc/images/emojis/musical_keyboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/musical_keyboard.png -------------------------------------------------------------------------------- /pc/images/emojis/no_bell.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/no_bell.png -------------------------------------------------------------------------------- /pc/images/emojis/no_bicycles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/no_bicycles.png -------------------------------------------------------------------------------- /pc/images/emojis/no_mobile_phones.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/no_mobile_phones.png -------------------------------------------------------------------------------- /pc/images/emojis/no_mouth.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/no_mouth.png -------------------------------------------------------------------------------- /pc/images/emojis/octocat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/octocat.png -------------------------------------------------------------------------------- /pc/images/emojis/on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/on.png -------------------------------------------------------------------------------- /pc/images/emojis/pensive.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/pensive.png -------------------------------------------------------------------------------- /pc/images/emojis/persevere.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/persevere.png -------------------------------------------------------------------------------- /pc/images/emojis/pray.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/pray.png -------------------------------------------------------------------------------- /pc/images/emojis/rabbit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/rabbit.png -------------------------------------------------------------------------------- /pc/images/emojis/relaxed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/relaxed.png -------------------------------------------------------------------------------- /pc/images/emojis/relieved.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/relieved.png -------------------------------------------------------------------------------- /pc/images/emojis/ribbon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/ribbon.png -------------------------------------------------------------------------------- /pc/images/emojis/scream.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/scream.png -------------------------------------------------------------------------------- /pc/images/emojis/scream_cat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/scream_cat.png -------------------------------------------------------------------------------- /pc/images/emojis/skull.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/skull.png -------------------------------------------------------------------------------- /pc/images/emojis/sleepy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/sleepy.png -------------------------------------------------------------------------------- /pc/images/emojis/smile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/smile.png -------------------------------------------------------------------------------- /pc/images/emojis/smile_cat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/smile_cat.png -------------------------------------------------------------------------------- /pc/images/emojis/smiley.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/smiley.png -------------------------------------------------------------------------------- /pc/images/emojis/smiley_cat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/smiley_cat.png -------------------------------------------------------------------------------- /pc/images/emojis/smirk.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/smirk.png -------------------------------------------------------------------------------- /pc/images/emojis/smirk_cat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/smirk_cat.png -------------------------------------------------------------------------------- /pc/images/emojis/snowflake.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/snowflake.png -------------------------------------------------------------------------------- /pc/images/emojis/snowman.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/snowman.png -------------------------------------------------------------------------------- /pc/images/emojis/sob.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/sob.png -------------------------------------------------------------------------------- /pc/images/emojis/spaghetti.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/spaghetti.png -------------------------------------------------------------------------------- /pc/images/emojis/sparkles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/sparkles.png -------------------------------------------------------------------------------- /pc/images/emojis/sparkling_heart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/sparkling_heart.png -------------------------------------------------------------------------------- /pc/images/emojis/squirrel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/squirrel.png -------------------------------------------------------------------------------- /pc/images/emojis/strawberry.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/strawberry.png -------------------------------------------------------------------------------- /pc/images/emojis/stuck_out_tongue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/stuck_out_tongue.png -------------------------------------------------------------------------------- /pc/images/emojis/stuck_out_tongue_closed_eyes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/stuck_out_tongue_closed_eyes.png -------------------------------------------------------------------------------- /pc/images/emojis/stuck_out_tongue_winking_eye.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/stuck_out_tongue_winking_eye.png -------------------------------------------------------------------------------- /pc/images/emojis/sunflower.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/sunflower.png -------------------------------------------------------------------------------- /pc/images/emojis/sunglasses.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/sunglasses.png -------------------------------------------------------------------------------- /pc/images/emojis/sunny.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/sunny.png -------------------------------------------------------------------------------- /pc/images/emojis/sweat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/sweat.png -------------------------------------------------------------------------------- /pc/images/emojis/sweat_drops.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/sweat_drops.png -------------------------------------------------------------------------------- /pc/images/emojis/sweat_smile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/sweat_smile.png -------------------------------------------------------------------------------- /pc/images/emojis/tada.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/tada.png -------------------------------------------------------------------------------- /pc/images/emojis/tiger.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/tiger.png -------------------------------------------------------------------------------- /pc/images/emojis/toilet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/toilet.png -------------------------------------------------------------------------------- /pc/images/emojis/turtle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/turtle.png -------------------------------------------------------------------------------- /pc/images/emojis/u7981.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/u7981.png -------------------------------------------------------------------------------- /pc/images/emojis/unamused.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/unamused.png -------------------------------------------------------------------------------- /pc/images/emojis/underage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/underage.png -------------------------------------------------------------------------------- /pc/images/emojis/up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/up.png -------------------------------------------------------------------------------- /pc/images/emojis/v.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/v.png -------------------------------------------------------------------------------- /pc/images/emojis/watermelon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/watermelon.png -------------------------------------------------------------------------------- /pc/images/emojis/whale.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/whale.png -------------------------------------------------------------------------------- /pc/images/emojis/whale2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/whale2.png -------------------------------------------------------------------------------- /pc/images/emojis/wink.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/wink.png -------------------------------------------------------------------------------- /pc/images/emojis/worried.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/worried.png -------------------------------------------------------------------------------- /pc/images/emojis/yum.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/yum.png -------------------------------------------------------------------------------- /pc/images/emojis/zap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/emojis/zap.png -------------------------------------------------------------------------------- /pc/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/favicon.ico -------------------------------------------------------------------------------- /pc/images/handbook/buy-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /pc/images/handbook/corner-mark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/handbook/corner-mark.png -------------------------------------------------------------------------------- /pc/images/handbook/dir.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /pc/images/handbook/finished.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /pc/images/handbook/menu.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Group 4 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /pc/images/handbook/more.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /pc/images/handbook/next.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /pc/images/handbook/poster-cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/handbook/poster-cover.png -------------------------------------------------------------------------------- /pc/images/handbook/prev.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /pc/images/handbook/star.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /pc/images/handbook/star2.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /pc/images/handbook/star_selected.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /pc/images/icon_comment_close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/icon_comment_close.png -------------------------------------------------------------------------------- /pc/images/icp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/icp.png -------------------------------------------------------------------------------- /pc/images/index/book1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/index/book1.jpg -------------------------------------------------------------------------------- /pc/images/index/book2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/index/book2.jpg -------------------------------------------------------------------------------- /pc/images/index/pin-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /pc/images/index/qrcode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/index/qrcode.png -------------------------------------------------------------------------------- /pc/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/logo.png -------------------------------------------------------------------------------- /pc/images/logo_backup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/logo_backup.png -------------------------------------------------------------------------------- /pc/images/message/post_nocontent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/message/post_nocontent.png -------------------------------------------------------------------------------- /pc/images/social.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/social.png -------------------------------------------------------------------------------- /pc/images/test/admin.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/test/admin.jpg -------------------------------------------------------------------------------- /pc/images/test/crawler.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/test/crawler.png -------------------------------------------------------------------------------- /pc/images/user/emptybox.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /pc/images/user/level/lv1.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /pc/images/zan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shen100/mili/dcac20058a190ead86121a6f8b39cad01fc2ee33/pc/images/zan.png -------------------------------------------------------------------------------- /pc/js/common/filters.js: -------------------------------------------------------------------------------- 1 | export const jobCompany = (user) => { 2 | let str = ''; 3 | if (user.job) { 4 | str = str + user.job; 5 | } 6 | if (user.job && user.company) { 7 | str = str + ' @ '; 8 | } 9 | if (user.company) { 10 | str = str + user.company; 11 | } 12 | return str; 13 | } 14 | 15 | export const levelImgURL = (level) => { 16 | const imgURL = { 17 | '1': 'lv1.svg', 18 | }[level] || 'lv1.svg'; 19 | return globalConfig.imgPath + '/user/level/' + imgURL; 20 | }; 21 | 22 | export const prettyCount = (count) => { 23 | count = count || 0; 24 | return count.toLocaleString(); 25 | } 26 | -------------------------------------------------------------------------------- /pc/js/components/comment/ReplyIcon.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pc/js/components/comment/ZanIcon.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | -------------------------------------------------------------------------------- /pc/js/components/common/ErrorTip.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 31 | 32 | -------------------------------------------------------------------------------- /pc/js/components/common/GlobalLoading.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | -------------------------------------------------------------------------------- /pc/js/components/common/More.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | -------------------------------------------------------------------------------- /pc/js/components/common/SuccessTip.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 36 | 37 | -------------------------------------------------------------------------------- /pc/js/components/common/sidemenu/CommonIcon.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 40 | 41 | -------------------------------------------------------------------------------- /pc/js/components/common/sidemenu/Icons.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 32 | 33 | -------------------------------------------------------------------------------- /pc/js/components/common/sidemenu/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import SideMenu from './SideMenu.vue'; 3 | 4 | import { 5 | Dropdown, 6 | DropdownItem, 7 | DropdownMenu, 8 | Icon, 9 | Menu, 10 | MenuItem, 11 | Tooltip, 12 | } from 'iview'; 13 | 14 | Vue.component('Dropdown', Dropdown); 15 | Vue.component('DropdownItem', DropdownItem); 16 | Vue.component('DropdownMenu', DropdownMenu); 17 | Vue.component('Icon', Icon); 18 | Vue.component('Menu', Menu); 19 | Vue.component('MenuItem', MenuItem); 20 | Vue.component('Tooltip', Tooltip); 21 | 22 | export default SideMenu; 23 | -------------------------------------------------------------------------------- /pc/js/components/common/sidemenu/itemmixin.js: -------------------------------------------------------------------------------- 1 | export default { 2 | props: { 3 | parentItem: { 4 | type: Object, 5 | default: () => {} 6 | }, 7 | theme: String, 8 | iconSize: Number 9 | }, 10 | computed: { 11 | parentName () { 12 | return this.parentItem.name; 13 | }, 14 | children () { 15 | return this.parentItem.children; 16 | }, 17 | textColor () { 18 | return this.theme === 'dark' ? '#fff' : '#495060'; 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /pc/js/components/common/sidemenu/mixin.js: -------------------------------------------------------------------------------- 1 | import CommonIcon from './CommonIcon.vue'; 2 | import { showTitle } from './util.js'; 3 | 4 | export default { 5 | components: { 6 | CommonIcon 7 | }, 8 | methods: { 9 | showTitle (item) { 10 | return showTitle(item, this); 11 | }, 12 | showChildren (item) { 13 | return item.children && (item.children.length > 1 || (item.meta && item.meta.showAlways)); 14 | }, 15 | getNameOrHref (item, children0) { 16 | return item.href ? `isTurnByHref_${item.href}` : (children0 ? item.children[0].name : item.name); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /pc/js/components/common/sidemenu/tools.js: -------------------------------------------------------------------------------- 1 | export const getUnion = (arr1, arr2) => { 2 | return Array.from(new Set([...arr1, ...arr2])); 3 | } 4 | -------------------------------------------------------------------------------- /pc/js/components/common/sidemenu/util.js: -------------------------------------------------------------------------------- 1 | export const showTitle = (item, vm) => { 2 | let { title, __titleIsFunction__ } = item.meta; 3 | if (!title) { 4 | return; 5 | } 6 | let useI18n = false; 7 | if (useI18n) { 8 | if (title.includes('{{') && title.includes('}}') && useI18n) { 9 | title = title.replace(/({{[\s\S]+?}})/, (m, str) => str.replace(/{{([\s\S]*)}}/, (m, _) => vm.$t(_.trim()))); 10 | } else if (__titleIsFunction__) { 11 | title = item.meta.title; 12 | } else { 13 | title = vm.$t(item.name); 14 | } 15 | } else { 16 | title = (item.meta && item.meta.title) || item.name; 17 | } 18 | return title; 19 | }; 20 | 21 | export const findNodeUpperByClasses = (ele, classes) => { 22 | let parentNode = ele.parentNode 23 | if (parentNode) { 24 | let classList = parentNode.classList 25 | if (classList && classes.every(className => classList.contains(className))) { 26 | return parentNode 27 | } else { 28 | return findNodeUpperByClasses(parentNode, classes) 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /pc/js/components/editor/MyHardBreak.js: -------------------------------------------------------------------------------- 1 | import { 2 | HardBreak, // br 3 | } from 'tiptap-extensions'; 4 | 5 | export default class extends HardBreak { 6 | get name () { 7 | return 'my_hard_break'; 8 | } 9 | 10 | commands ({ type, }) { 11 | return attrs => (state, dispatch) => { 12 | const { selection, } = state; 13 | let position = selection.$cursor ? selection.$cursor.pos : selection.$to.pos; 14 | if (attrs && attrs.isCodeBlock) { 15 | // 插入代码块时,光标在代码块中,这时在position + 1处插入br 16 | position = position + 1; 17 | } 18 | const node = type.create(attrs); 19 | const transaction = state.tr.insert(position, node); 20 | dispatch(transaction); 21 | }; 22 | } 23 | }; 24 | -------------------------------------------------------------------------------- /pc/js/components/editor/tui-editor-ext.js: -------------------------------------------------------------------------------- 1 | import Editor from 'tui-editor'; 2 | 3 | Editor.defineExtension('warning', function() { 4 | Editor.codeBlockManager.setReplacer('!', function (code) { 5 | return `
${code}
`; 6 | }); 7 | }); -------------------------------------------------------------------------------- /pc/js/components/messages/Chat.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 20 | -------------------------------------------------------------------------------- /pc/js/components/messages/Comment.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 27 | -------------------------------------------------------------------------------- /pc/js/components/messages/Follow.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 27 | -------------------------------------------------------------------------------- /pc/js/components/messages/Like.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 27 | -------------------------------------------------------------------------------- /pc/js/components/messages/Money.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 27 | -------------------------------------------------------------------------------- /pc/js/components/messages/Other.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 27 | -------------------------------------------------------------------------------- /pc/js/constants/article.js: -------------------------------------------------------------------------------- 1 | export const ArticleContentType = { 2 | Markdown: 1, 3 | HTML: 2, 4 | }; 5 | 6 | export const MarkedConstants = { 7 | options: { 8 | gfm: true, 9 | breaks: true, 10 | } 11 | } -------------------------------------------------------------------------------- /pc/js/constants/book.js: -------------------------------------------------------------------------------- 1 | export const BookStatus = { 2 | // 未发布 3 | BookUnpublish: 0, 4 | 5 | // 已发布 6 | BookPublished: 1, 7 | }; -------------------------------------------------------------------------------- /pc/js/constants/entity.js: -------------------------------------------------------------------------------- 1 | export const ArticleCollectionStatus = { 2 | Auditing: 1, 3 | Collected: 2, 4 | NotCollect: 3, 5 | }; 6 | 7 | export const UserSex = { 8 | Male: 0, // 男 9 | Female: 1, // 女 10 | Unknown: 2, // 未知 11 | }; 12 | -------------------------------------------------------------------------------- /pc/js/constants/error.js: -------------------------------------------------------------------------------- 1 | export const ErrorCode = { 2 | SUCCESS: { CODE: 0, MESSAGE: 'success', }, 3 | ERROR: { CODE: 1, MESSAGE: 'fail', }, 4 | ParamsError: { CODE: 2, MESSAGE: '参数错误', }, 5 | 6 | Forbidden: { CODE: 403, MESSAGE: '没有权限执行此操作', }, 7 | NotFound: { CODE: 404, MESSAGE: '找不到请求的资源', }, 8 | 9 | LoginError: { CODE: 1000, MESSAGE: '用户名或密码错误', }, 10 | LoginTimeout: { CODE: 1001, MESSAGE: '登录超时', }, 11 | InActive: { CODE: 1002, MESSAGE: '账号未激活', }, 12 | 13 | TokenError: { CODE: 1003, MESSAGE: 'token错误', }, 14 | Frozen: { CODE: 1004, MESSAGE: '账号已冻结', }, 15 | 16 | InvalidUserName: { CODE: 1005, MESSAGE: '无效的用户名', }, 17 | InvalidPhone: { CODE: 1006, MESSAGE: '无效的手机号', }, 18 | InvalidCaptcha: { CODE: 1007, MESSAGE: '无效的验证码', }, 19 | InvalidPassword: { CODE: 1008, MESSAGE: '无效的密码', }, 20 | UserNameExists: { CODE: 1009, MESSAGE: '用户名已存在', }, 21 | PhoneExists: { CODE: 1010, MESSAGE: '手机号已存在', }, 22 | }; 23 | -------------------------------------------------------------------------------- /pc/js/constants/exercise.js: -------------------------------------------------------------------------------- 1 | export const QuestionType = { 2 | Radio: 1, // 单选 3 | Multiple: 2, // 多选 4 | }; 5 | 6 | export const QuestionConstants = { 7 | // 最多可以有多少个选项 8 | OptionMaxCount: 10, 9 | }; -------------------------------------------------------------------------------- /pc/js/entry/boilingpoint/boilingpoint.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 沸点列表,沸点详情页面都用的这个js 3 | */ 4 | import 'iview/dist/styles/iview.css'; 5 | import '~/styles/main.scss'; 6 | import '~/styles/boilingpoint/boilingpoint.scss'; 7 | import '~/styles/comment/commentDisplay.scss'; 8 | import '~/js/common/default.js'; 9 | import Vue from 'vue'; 10 | import BoilingPoint from '~/js/pages/boilingpoint/BoilingPoint.vue'; 11 | 12 | import { 13 | Modal, 14 | Radio, 15 | RadioGroup, 16 | } from 'iview'; 17 | 18 | Vue.component('Modal', Modal); 19 | Vue.component('Radio', Radio); 20 | Vue.component('RadioGroup', RadioGroup); 21 | 22 | import { 23 | registerDirective, 24 | } from '~/js/utils/vue.js'; 25 | 26 | registerDirective(Vue); 27 | 28 | new Vue({ 29 | render: h => h(BoilingPoint, { 30 | props: { 31 | url: `/boilingpoints/${window.boilingPointType}`, 32 | topicID: window.topicID, 33 | userID: window.user && window.user.id || undefined, 34 | user: window.user, // 当前登录用户 35 | boilingPoint: window.boilingPoint, 36 | commentsVisible: window.commentsVisible, 37 | } 38 | }), 39 | }).$mount('#centerBox'); 40 | -------------------------------------------------------------------------------- /pc/js/entry/book/books.js: -------------------------------------------------------------------------------- 1 | import '~/styles/main.scss'; 2 | import '~/styles/book/books.scss'; 3 | import '~/js/common/default.js'; 4 | import Vue from 'vue'; 5 | import Books from '~/js/pages/book/Books.vue'; 6 | import { 7 | getScrollPos 8 | } from '~/js/utils/dom.js'; 9 | 10 | new Vue({ 11 | render: h => h(Books), 12 | }).$mount('#books'); 13 | 14 | // 右侧悬浮 15 | (function() { 16 | function setArticleSidebarPosition() { 17 | const sidebar = document.getElementsByClassName('side-box')[0]; 18 | const scrollTop = getScrollPos().scrollTop; 19 | let top = 124 - scrollTop; 20 | top = top > 57 + 20 ? top : 57 + 20; 21 | sidebar.style.top = top + 'px'; 22 | } 23 | 24 | window.addEventListener('scroll', function() { 25 | setArticleSidebarPosition(); 26 | }); 27 | window.addEventListener('resize', function() { 28 | setArticleSidebarPosition(); 29 | }); 30 | }()); 31 | 32 | -------------------------------------------------------------------------------- /pc/js/entry/book/chapter.js: -------------------------------------------------------------------------------- 1 | import '~/styles/main.scss'; 2 | import '~/styles/article/articleDisplay.scss'; 3 | import '~/styles/book/chapter.scss'; 4 | import '~/styles/comment/commentDisplay.scss'; 5 | import Vue from 'vue'; 6 | import VueRouter from 'vue-router'; 7 | import ChapterLayout from '~/js/pages/book/ChapterLayout.vue'; 8 | 9 | Vue.use(VueRouter); 10 | 11 | import { 12 | registerDirective, 13 | } from '~/js/utils/vue.js'; 14 | 15 | registerDirective(Vue); 16 | 17 | const routes = [ 18 | { path: '/books/:bookID/chapters/:chapterID', component: () => import('~/js/pages/book/Chapter.vue') }, 19 | ]; 20 | 21 | const router = new VueRouter({ 22 | routes, 23 | mode: 'history', 24 | scrollBehavior (to, from, savedPosition) { 25 | if (savedPosition) { 26 | return savedPosition; 27 | } else { 28 | return { x: 0, y: 0 }; 29 | } 30 | } 31 | }); 32 | 33 | new Vue({ 34 | router, 35 | render: h => h(ChapterLayout), 36 | }).$mount('#chapterBox'); 37 | -------------------------------------------------------------------------------- /pc/js/entry/codeStats.js: -------------------------------------------------------------------------------- 1 | import 'iview/dist/styles/iview.css'; 2 | import '~/styles/main.scss'; 3 | import '~/styles/codeStats.scss'; 4 | import '~/js/common/default.js'; 5 | import Vue from 'vue'; 6 | import App from '~/js/pages/CodeStats.vue'; 7 | import { 8 | Tree, 9 | Row, 10 | Col, 11 | RadioGroup, 12 | Radio, 13 | } from 'iview'; 14 | 15 | Vue.component('Tree', Tree); 16 | Vue.component('Row', Row); 17 | Vue.component('Col', Col); 18 | Vue.component('RadioGroup', RadioGroup); 19 | Vue.component('Radio', Radio); 20 | 21 | new Vue({ 22 | render: h => h(App), 23 | }).$mount('#app'); -------------------------------------------------------------------------------- /pc/js/entry/collection/edit.js: -------------------------------------------------------------------------------- 1 | import 'iview/dist/styles/iview.css'; 2 | import '~/styles/main.scss'; 3 | import '~/js/common/default.js'; 4 | import Vue from 'vue'; 5 | import App from '~/js/components/collection/edit.vue'; 6 | import { 7 | Upload, 8 | } from 'iview'; 9 | 10 | Vue.component('Upload', Upload); 11 | new Vue({ 12 | render: h => h(App), 13 | }).$mount('#app'); 14 | -------------------------------------------------------------------------------- /pc/js/entry/editor/drafts.js: -------------------------------------------------------------------------------- 1 | import '~/styles/main.scss'; 2 | import '~/styles/editor/drafts.scss'; 3 | import Vue from 'vue'; 4 | import { 5 | Modal, 6 | } from 'iview'; 7 | import App from '~/js/pages/editor/Drafts.vue'; 8 | 9 | import { 10 | registerDirective, 11 | } from '~/js/utils/vue.js'; 12 | 13 | registerDirective(Vue); 14 | 15 | Vue.component('Modal', Modal); 16 | 17 | new Vue({ 18 | render: h => h(App), 19 | }).$mount('#app'); 20 | 21 | if (module.hot) { 22 | module.hot.accept(); 23 | } 24 | -------------------------------------------------------------------------------- /pc/js/entry/editor/editMarkdownDraft.js: -------------------------------------------------------------------------------- 1 | import 'iview/dist/styles/iview.css'; 2 | import '~/styles/main.scss'; 3 | import '~/styles/editor/editDraft.scss'; 4 | 5 | import Vue from 'vue'; 6 | import { 7 | Modal, 8 | Select, 9 | Option, 10 | } from 'iview'; 11 | import App from '~/js/pages/editor/EditMarkdownDraft.vue'; 12 | 13 | import { 14 | registerDirective, 15 | } from '~/js/utils/vue.js'; 16 | 17 | registerDirective(Vue); 18 | 19 | Vue.component('Modal', Modal); 20 | Vue.component('Select', Select); 21 | Vue.component('Option', Option); 22 | 23 | new Vue({ 24 | render: h => h(App), 25 | }).$mount('#app'); 26 | 27 | if (module.hot) { 28 | module.hot.accept(); 29 | } 30 | -------------------------------------------------------------------------------- /pc/js/entry/editor/editRichDraft.js: -------------------------------------------------------------------------------- 1 | import 'iview/dist/styles/iview.css'; 2 | import '~/styles/main.scss'; 3 | import '~/styles/editor/editDraft.scss'; 4 | import '~/styles/editor/rich.editor.scss'; 5 | 6 | import Vue from 'vue'; 7 | import { 8 | Modal, 9 | Select, 10 | Option, 11 | } from 'iview'; 12 | import App from '~/js/pages/editor/EditRichDraft.vue'; 13 | 14 | import { 15 | registerDirective, 16 | } from '~/js/utils/vue.js'; 17 | 18 | registerDirective(Vue); 19 | 20 | Vue.component('Modal', Modal); 21 | Vue.component('Select', Select); 22 | Vue.component('Option', Option); 23 | 24 | new Vue({ 25 | render: h => h(App), 26 | }).$mount('#app'); 27 | 28 | if (module.hot) { 29 | module.hot.accept(); 30 | } 31 | -------------------------------------------------------------------------------- /pc/js/entry/editor/published.js: -------------------------------------------------------------------------------- 1 | import '~/styles/main.scss'; 2 | import Vue from 'vue'; 3 | import '~/js/common/default.js'; 4 | import App from '~/js/pages/editor/Published.vue'; 5 | 6 | import { 7 | Modal, 8 | } from 'iview'; 9 | 10 | Vue.component('Modal', Modal); 11 | 12 | new Vue({ 13 | render: h => h(App), 14 | }).$mount('#app'); 15 | 16 | if (module.hot) { 17 | module.hot.accept(); 18 | } 19 | -------------------------------------------------------------------------------- /pc/js/entry/handbook/handbookDetail.js: -------------------------------------------------------------------------------- 1 | import '~/styles/main.scss'; 2 | import '~/styles/handbook/handbookDetail.scss'; 3 | import '~/js/common/default.js'; 4 | import Vue from 'vue'; 5 | 6 | import { 7 | registerDirective, 8 | } from '~/js/utils/vue.js'; 9 | 10 | registerDirective(Vue); 11 | -------------------------------------------------------------------------------- /pc/js/entry/handbook/handbooks.js: -------------------------------------------------------------------------------- 1 | import '~/styles/main.scss'; 2 | import '~/styles/handbook/handbooks.scss'; 3 | import '~/js/common/default.js'; 4 | import Vue from 'vue'; 5 | 6 | import { 7 | registerDirective, 8 | } from '~/js/utils/vue.js'; 9 | 10 | registerDirective(Vue); 11 | -------------------------------------------------------------------------------- /pc/js/entry/index.js: -------------------------------------------------------------------------------- 1 | import '~/styles/main.scss'; 2 | import '~/styles/index.css'; 3 | import '~/js/common/default.js'; 4 | import Vue from 'vue'; 5 | import Index from '~/js/pages/Index.vue'; 6 | 7 | import { 8 | getScrollPos 9 | } from '~/js/utils/dom.js'; 10 | 11 | import { 12 | registerDirective, 13 | } from '~/js/utils/vue.js'; 14 | 15 | registerDirective(Vue); 16 | 17 | new Vue({ 18 | render: h => h(Index), 19 | }).$mount('#articleList'); 20 | 21 | // 右侧悬浮 22 | (function() { 23 | function setRightSidebarPosition() { 24 | const sidebar = document.getElementsByClassName('home-sidebar')[0]; 25 | const scrollTop = getScrollPos().scrollTop; 26 | let top = 124 - scrollTop; 27 | top = top > 57 + 20 ? top : 57 + 20; 28 | sidebar.style.top = top + 'px'; 29 | } 30 | 31 | window.addEventListener('scroll', function() { 32 | setRightSidebarPosition(); 33 | }); 34 | window.addEventListener('resize', function() { 35 | setRightSidebarPosition(); 36 | }); 37 | }()); 38 | -------------------------------------------------------------------------------- /pc/js/entry/recommendations/authors.js: -------------------------------------------------------------------------------- 1 | import '~/styles/main.scss'; 2 | import '~/js/common/default.js'; 3 | import Vue from 'vue'; 4 | import VueRouter from 'vue-router'; 5 | import Layout from '~/js/pages/recommendations/Layout.vue'; 6 | 7 | Vue.use(VueRouter); 8 | 9 | const routes = [ 10 | { 11 | path: '/recommendations/authors/recommended', 12 | component: () => import('~/js/pages/recommendations/AuthorView.vue') 13 | } 14 | ]; 15 | 16 | for (let i = 0; i < window.categories.length; i++) { 17 | routes.push({ 18 | path: `/recommendations/authors/${window.categories[i].pathname}`, 19 | component: () => import('~/js/pages/recommendations/AuthorView.vue') 20 | }); 21 | } 22 | 23 | const router = new VueRouter({ 24 | routes, 25 | mode: 'history', 26 | scrollBehavior (to, from, savedPosition) { 27 | if (savedPosition) { 28 | return savedPosition; 29 | } else { 30 | return { x: 0, y: 0 }; 31 | } 32 | } 33 | }); 34 | 35 | const app = new Vue({ 36 | router, 37 | render: h => h(Layout), 38 | }).$mount('#mainBox'); -------------------------------------------------------------------------------- /pc/js/entry/search/search.js: -------------------------------------------------------------------------------- 1 | import '~/styles/main.scss'; 2 | import '~/styles/search/search.scss'; 3 | import '~/js/common/default.js'; 4 | import Vue from 'vue'; 5 | import All from '~/js/pages/search/All.vue'; 6 | import Article from '~/js/pages/search/Article.vue'; 7 | import Category from '~/js/pages/search/Category.vue'; 8 | import User from '~/js/pages/search/User.vue'; 9 | 10 | let AppView; 11 | 12 | switch (window.searchType) { 13 | case 'all': { 14 | AppView = All; 15 | break; 16 | } 17 | case 'article': { 18 | AppView = Article; 19 | break; 20 | } 21 | case 'category': { 22 | AppView = Category; 23 | break; 24 | } 25 | case 'user': { 26 | AppView = User; 27 | break; 28 | } 29 | } 30 | 31 | new Vue({ 32 | render: h => h(AppView), 33 | }).$mount('#search-view'); 34 | -------------------------------------------------------------------------------- /pc/js/entry/settings/settings.js: -------------------------------------------------------------------------------- 1 | import 'iview/dist/styles/iview.css'; 2 | import '~/styles/main.scss'; 3 | import '~/styles/settings/settings.scss'; 4 | import '~/js/common/default.js'; 5 | import Vue from 'vue'; 6 | import VueRouter from 'vue-router'; 7 | import Layout from '~/js/pages/settings/Layout.vue'; 8 | 9 | import { 10 | registerDirective, 11 | } from '~/js/utils/vue.js'; 12 | 13 | registerDirective(Vue); 14 | 15 | Vue.use(VueRouter); 16 | 17 | const routes = [ 18 | { path: '/settings/profile', component: () => import('~/js/pages/settings/ProfileView.vue') }, 19 | { path: '/settings/password', component: () => import('~/js/pages/settings/PasswordView.vue') }, 20 | ]; 21 | 22 | const router = new VueRouter({ 23 | routes, 24 | mode: 'history', 25 | scrollBehavior (to, from, savedPosition) { 26 | if (savedPosition) { 27 | return savedPosition; 28 | } else { 29 | return { x: 0, y: 0 }; 30 | } 31 | } 32 | }); 33 | 34 | const app = new Vue({ 35 | router, 36 | render: h => h(Layout), 37 | }).$mount('#mainBox'); -------------------------------------------------------------------------------- /pc/js/entry/signin.js: -------------------------------------------------------------------------------- 1 | import '~/styles/main.scss'; 2 | import '~/styles/signin.css'; 3 | import '~/js/common/default.js'; 4 | import Vue from 'vue'; 5 | import App from '~/js/pages/SigninApp.vue'; 6 | 7 | new Vue({ 8 | render: h => h(App), 9 | }).$mount('#app'); -------------------------------------------------------------------------------- /pc/js/entry/signup.js: -------------------------------------------------------------------------------- 1 | import '~/styles/main.scss'; 2 | import '~/styles/signup.css'; 3 | import '~/js/common/default.js'; 4 | import Vue from 'vue'; 5 | import App from '~/js/pages/SignupApp.vue'; 6 | 7 | new Vue({ 8 | render: h => h(App), 9 | }).$mount('#app'); -------------------------------------------------------------------------------- /pc/js/entry/tag/tag.js: -------------------------------------------------------------------------------- 1 | import '~/styles/main.scss'; 2 | import '~/js/common/default.js'; 3 | import Vue from 'vue'; 4 | import App from '~/js/pages/tag/Tag.vue'; 5 | 6 | new Vue({ 7 | render: h => h(App), 8 | }).$mount('#container'); 9 | -------------------------------------------------------------------------------- /pc/js/entry/tag/tagDetail.js: -------------------------------------------------------------------------------- 1 | import '~/styles/main.scss'; 2 | import '~/styles/tag/tagDetail.scss'; 3 | import '~/js/common/default.js'; 4 | import Vue from 'vue'; 5 | import TagDetail from '~/js/pages/tag/TagDetail.vue'; 6 | 7 | new Vue({ 8 | render: h => h(TagDetail), 9 | }).$mount('#tag-entry-list-box'); 10 | -------------------------------------------------------------------------------- /pc/js/pages/Messages.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 20 | -------------------------------------------------------------------------------- /pc/js/pages/admin/404.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pc/js/pages/admin/exercise/ExerciseList.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | -------------------------------------------------------------------------------- /pc/js/pages/admin/index.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pc/js/pages/admin/test.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pc/js/pages/handbook/EditHandBookLayout.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 15 | -------------------------------------------------------------------------------- /pc/js/pages/settings/Layout.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 24 | 25 | -------------------------------------------------------------------------------- /pc/js/pages/user/BoilingPointView.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 36 | 37 | -------------------------------------------------------------------------------- /pc/js/pages/user/CollectionView.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | -------------------------------------------------------------------------------- /pc/js/utils/vue.js: -------------------------------------------------------------------------------- 1 | export const registerDirective = (Vue) => { 2 | if (!Vue.directive('clickoutside')) { 3 | Vue.directive('clickoutside', { 4 | bind (el, binding, vnode) { 5 | function documentHandler (event) { 6 | if (el.contains(event.target)) { 7 | return false; 8 | } 9 | if (binding.expression) { 10 | binding.value(event, el.dataset); 11 | } 12 | } 13 | el.__vueClickOutside__ = documentHandler; 14 | document.addEventListener('click', documentHandler); 15 | }, 16 | unbind (el, binding) { 17 | document.removeEventListener('click', el.__vueClickOutside__); 18 | delete el.__vueClickOutside__; 19 | }, 20 | }); 21 | } 22 | 23 | if (!Vue.directive('highlight')) { 24 | Vue.directive('highlight', (el) => { 25 | const blocks = el.querySelectorAll('pre code'); 26 | blocks.forEach((block) => { 27 | hljs.highlightBlock(block); 28 | }); 29 | }); 30 | } 31 | }; 32 | -------------------------------------------------------------------------------- /pc/styles/boilingpoint/boilingpointDetail.scss: -------------------------------------------------------------------------------- 1 | .main-box .stream { 2 | width: 640px; 3 | } 4 | 5 | .boiling-sidebar { 6 | width: 300px; 7 | max-width: 300px; 8 | margin-left: 660px; 9 | } 10 | 11 | .main-box { 12 | margin-bottom: 60px; 13 | } -------------------------------------------------------------------------------- /pc/styles/comment/commentDisplay.scss: -------------------------------------------------------------------------------- 1 | .sub-comment-wrap p { 2 | display: inline; 3 | } 4 | 5 | 6 | .comment-wrap img, .sub-comment-wrap img { 7 | width: 20px; 8 | height: 20px; 9 | display: inline-block; 10 | vertical-align: top; 11 | } 12 | 13 | .comment-wrap p { 14 | margin: 0; 15 | margin-bottom: 2px; 16 | line-height: 20px; 17 | font-size: 13px; 18 | word-break: break-word!important; 19 | word-break: break-all; 20 | } -------------------------------------------------------------------------------- /pc/styles/libs/highlight.codestyle.css: -------------------------------------------------------------------------------- 1 | .hljs{display:block;overflow-x:auto;padding:0.5em;background:#fdf6e3;color:#657b83}.hljs-comment,.hljs-quote{color:#93a1a1}.hljs-keyword,.hljs-selector-tag,.hljs-addition{color:#859900}.hljs-number,.hljs-string,.hljs-meta .hljs-meta-string,.hljs-literal,.hljs-doctag,.hljs-regexp{color:#2aa198}.hljs-title,.hljs-section,.hljs-name,.hljs-selector-id,.hljs-selector-class{color:#268bd2}.hljs-attribute,.hljs-attr,.hljs-variable,.hljs-template-variable,.hljs-class .hljs-title,.hljs-type{color:#b58900}.hljs-symbol,.hljs-bullet,.hljs-subst,.hljs-meta,.hljs-meta .hljs-keyword,.hljs-selector-attr,.hljs-selector-pseudo,.hljs-link{color:#cb4b16}.hljs-built_in,.hljs-deletion{color:#dc322f}.hljs-formula{background:#eee8d5}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:bold} 2 | -------------------------------------------------------------------------------- /pc/styles/libs/highlight.min.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Solarized Light 3 | */ 4 | .hljs{display:block;overflow-x:auto;padding:0.5em;background:#fdf6e3;color:#657b83}.hljs-comment,.hljs-quote{color:#93a1a1}.hljs-keyword,.hljs-selector-tag,.hljs-addition{color:#859900}.hljs-number,.hljs-string,.hljs-meta .hljs-meta-string,.hljs-literal,.hljs-doctag,.hljs-regexp{color:#2aa198}.hljs-title,.hljs-section,.hljs-name,.hljs-selector-id,.hljs-selector-class{color:#268bd2}.hljs-attribute,.hljs-attr,.hljs-variable,.hljs-template-variable,.hljs-class .hljs-title,.hljs-type{color:#b58900}.hljs-symbol,.hljs-bullet,.hljs-subst,.hljs-meta,.hljs-meta .hljs-keyword,.hljs-selector-attr,.hljs-selector-pseudo,.hljs-link{color:#cb4b16}.hljs-built_in,.hljs-deletion{color:#dc322f}.hljs-formula{background:#eee8d5}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:bold} 5 | -------------------------------------------------------------------------------- /pc/styles/settings/settings.scss: -------------------------------------------------------------------------------- 1 | body { 2 | background-color: #f4f5f5; 3 | } 4 | 5 | .view-nav { 6 | position: fixed; 7 | background-color: #fff; 8 | top: 57px; 9 | width: 100%; 10 | height: 46px; 11 | z-index: 100; 12 | box-shadow: 0 1px 2px 0 rgba(0, 0, 0, .05); 13 | transition: all .2s; 14 | transform: translateZ(0); 15 | } 16 | 17 | .view-nav .nav-list { 18 | max-width: 960px; 19 | height: 100%; 20 | margin: auto; 21 | display: flex; 22 | align-items: center; 23 | line-height: 1; 24 | } 25 | 26 | .view-nav .nav-list .nav-item { 27 | height: 100%; 28 | align-items: center; 29 | display: flex; 30 | flex-shrink: 0; 31 | font-size: 14px; 32 | padding: 0 12px; 33 | position: relative; 34 | cursor: pointer; 35 | } 36 | 37 | .view-nav .nav-list .nav-item a { 38 | color: rgb(113, 119, 124); 39 | line-height: 46px; 40 | } 41 | 42 | .view-nav .nav-list .nav-item a:hover { 43 | text-decoration: none; 44 | } 45 | 46 | .view-nav .nav-list .nav-item .active { 47 | color: #ea6f5a; 48 | } 49 | 50 | .view-nav .nav-list .nav-item:first-child { 51 | padding: 0 12px 0 0; 52 | } -------------------------------------------------------------------------------- /pc/styles/variable.scss: -------------------------------------------------------------------------------- 1 | $fontFamily: -apple-system,SF UI Text,Arial,PingFang SC,Hiragino Sans GB,Microsoft YaHei,WenQuanYi Micro Hei,sans-serif; 2 | $bgMask: rgba(0, 0, 0, 0.6); -------------------------------------------------------------------------------- /src/admin/admin.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { ArticleController } from './article.controller'; 3 | import { UserModule } from '../user/user.module'; 4 | import { CMSModule } from '../cms/cms.module'; 5 | import { IndexController } from './index.controller'; 6 | import { CommonModule } from '../common/common.module'; 7 | 8 | @Module({ 9 | imports: [ 10 | CMSModule, 11 | UserModule, 12 | CommonModule, 13 | ], 14 | controllers: [ 15 | ArticleController, 16 | IndexController, 17 | ], 18 | providers: [ 19 | ], 20 | }) 21 | export class AdminModule {} -------------------------------------------------------------------------------- /src/admin/article.controller.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Controller, 3 | Put, 4 | UseGuards, 5 | Param, 6 | } from '@nestjs/common'; 7 | 8 | import { RolesGuard } from '../core/guards/roles.guard'; 9 | import { Roles } from '../core/decorators/roles.decorator'; 10 | import { UserRole } from '../entity/user.entity'; 11 | import { ActiveGuard } from '../core/guards/active.guard'; 12 | import { ArticleService } from '../cms/article.service'; 13 | import { AdminAPIPrefix } from '../constants/constants'; 14 | 15 | @Controller() 16 | @Roles(UserRole.Editor, UserRole.Admin, UserRole.SuperAdmin) 17 | @UseGuards(ActiveGuard, RolesGuard) 18 | export class ArticleController { 19 | constructor( 20 | private readonly articleService: ArticleService, 21 | ) {} 22 | 23 | @Put(`${AdminAPIPrefix}/articles/allverifyfail/:userID`) 24 | @Roles(UserRole.Editor, UserRole.Admin, UserRole.SuperAdmin) 25 | async allVerifyFail(@Param('userID') userID: number) { 26 | await Promise.all([ 27 | this.articleService.allVerifyFail(userID), 28 | ]); 29 | return { 30 | }; 31 | } 32 | } -------------------------------------------------------------------------------- /src/admin/index.controller.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Controller, 3 | UseGuards, 4 | Get, 5 | Res, 6 | Req, 7 | } from '@nestjs/common'; 8 | import { ActiveGuard } from '../core/guards/active.guard'; 9 | import { RolesGuard } from '../core/guards/roles.guard'; 10 | import { UserRole } from '../entity/user.entity'; 11 | import { AdminPageURL } from '../constants/constants'; 12 | import { Roles } from '../core/decorators/roles.decorator'; 13 | import { MyLoggerService } from '../common/logger.service'; 14 | 15 | @Controller() 16 | export class IndexController { 17 | constructor( 18 | private readonly logger: MyLoggerService, 19 | ) {} 20 | 21 | @Get(`${AdminPageURL}/*`) 22 | @UseGuards(ActiveGuard, RolesGuard) 23 | @Roles(UserRole.Editor, UserRole.Admin, UserRole.SuperAdmin) 24 | async adminIndex(@Req() req, @Res() res) { 25 | this.logger.info({ 26 | data: { 27 | middleware: 'adminIndex', 28 | ip: req.clientIp, 29 | }, 30 | }); 31 | res.render('pages/admin/app', { 32 | adminPageURL: AdminPageURL, 33 | }); 34 | } 35 | } -------------------------------------------------------------------------------- /src/boilingpoint/dto/edit-topic.dto.ts: -------------------------------------------------------------------------------- 1 | import { 2 | IsString, 3 | IsInt, 4 | MinLength, 5 | MaxLength, 6 | Min, 7 | ValidateIf, 8 | IsUrl, 9 | } from 'class-validator'; 10 | import { BoilingPointConstants } from '../../constants/boilingpoint'; 11 | 12 | export class EditTopicDto { 13 | // 传了id字段的话,才对id进行检验 14 | @ValidateIf(obj => { 15 | return obj && typeof obj.id !== 'undefined'; 16 | }) 17 | @IsInt({ 18 | message: '无效的id', 19 | }) 20 | id: number; 21 | 22 | @MinLength(1, { 23 | message: '名称不能为空', 24 | }) 25 | @MaxLength(BoilingPointConstants.MAX_TOPIC_TITLE_LENGTH, { 26 | message: '名称不能超过 $constraint1 个字符', 27 | }) 28 | @IsString() 29 | readonly name: string; 30 | 31 | @IsUrl({ 32 | protocols: ['https'], 33 | require_protocol: true, 34 | }) 35 | readonly icon: string; 36 | 37 | @Min(1) 38 | @IsInt({ 39 | message: '无效的序号', 40 | }) 41 | readonly sequence: number; 42 | } -------------------------------------------------------------------------------- /src/boilingpoint/topic.admin.controller.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Controller, Post, Body, Get, Put, Param, 3 | } from '@nestjs/common'; 4 | import { AdminAPIPrefix } from '../constants/constants'; 5 | import { EditTopicDto } from './dto/edit-topic.dto'; 6 | import { TopicService } from './topic.service'; 7 | import { MustIntPipe } from '../core/pipes/must-int.pipe'; 8 | 9 | @Controller() 10 | export class TopicAdminController { 11 | constructor( 12 | private readonly topicService: TopicService, 13 | ) {} 14 | 15 | /** 16 | * 编辑沸点话题 17 | */ 18 | @Put(`${AdminAPIPrefix}/boilingpoint/topics/:id`) 19 | async update(@Body() editTopicDto: EditTopicDto, @Param('id', MustIntPipe) id: number) { 20 | editTopicDto.id = id; 21 | await this.topicService.update(editTopicDto); 22 | return { 23 | }; 24 | } 25 | 26 | /** 27 | * 创建沸点话题 28 | */ 29 | @Post(`${AdminAPIPrefix}/boilingpoint/topics`) 30 | async create(@Body() editTopicDto: EditTopicDto) { 31 | await this.topicService.create(editTopicDto); 32 | return {}; 33 | } 34 | } -------------------------------------------------------------------------------- /src/boilingpoint/topic.controller.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Controller, Get, 3 | } from '@nestjs/common'; 4 | import { APIPrefix } from '../constants/constants'; 5 | import { TopicService } from './topic.service'; 6 | 7 | @Controller() 8 | export class TopicController { 9 | constructor( 10 | private readonly topicService: TopicService, 11 | ) {} 12 | 13 | /** 14 | * 所有的沸点话题 15 | */ 16 | @Get(`${APIPrefix}/boilingpoint/topics`) 17 | async list() { 18 | const topics = await this.topicService.list(); 19 | return { 20 | topics, 21 | }; 22 | } 23 | } -------------------------------------------------------------------------------- /src/book/dto/create-book-chapter.dto.ts: -------------------------------------------------------------------------------- 1 | import { 2 | IsString, 3 | MinLength, 4 | MaxLength, 5 | IsInt, 6 | ValidateIf, 7 | } from 'class-validator'; 8 | import { BookConstants } from '../../constants/book'; 9 | 10 | export class CreateBookChapterDto { 11 | @MinLength(1, { 12 | message: '章节标题不能为空', 13 | }) 14 | @MaxLength(BookConstants.MAX_CHAPTER_TITLE_LENGTH, { 15 | message: '章节标题不能超过 $constraint1 个字符', 16 | }) 17 | @IsString() 18 | readonly name: string; 19 | 20 | @IsInt({ 21 | message: '无效的bookID', 22 | }) 23 | readonly bookID: number; 24 | 25 | // 传了 parentChapterID 字段的话,才对 parentChapterID 进行检验 26 | @ValidateIf(obj => { 27 | return obj && typeof obj.parentChapterID !== 'undefined'; 28 | }) 29 | @IsInt({ 30 | message: '无效的id', 31 | }) 32 | readonly parentChapterID: number; 33 | } -------------------------------------------------------------------------------- /src/book/dto/create-book-star.dto.ts: -------------------------------------------------------------------------------- 1 | import { 2 | IsInt, MaxLength, IsString, ValidateIf, Min, Max, 3 | } from 'class-validator'; 4 | import { BookConstants } from '../../constants/book'; 5 | 6 | export class CreateBookStarDto { 7 | @IsInt({ 8 | message: '无效的图书id', 9 | }) 10 | readonly bookID: number; 11 | 12 | @ValidateIf(obj => { 13 | return obj && typeof obj.htmlContent !== 'undefined'; 14 | }) 15 | @MaxLength(BookConstants.MAX_STAR_CONTENT_LENGTH, { 16 | message: '评价内容不能超过 $constraint1 个字符', 17 | }) 18 | @IsString() 19 | readonly htmlContent: string; 20 | 21 | @IsInt() 22 | @Min(BookConstants.MIN_STAR_VALUE, { 23 | message: '无效的评分', 24 | }) 25 | @Max(BookConstants.MAX_STAR_VALUE, { 26 | message: '无效的评分', 27 | }) 28 | readonly star: number; 29 | } -------------------------------------------------------------------------------- /src/book/dto/create-book.dto.ts: -------------------------------------------------------------------------------- 1 | import { 2 | MaxLength, IsString, MinLength, IsEnum, IsUrl, 3 | } from 'class-validator'; 4 | import { BookConstants } from '../../constants/book'; 5 | import { ArticleContentType } from '../../entity/article.entity'; 6 | 7 | export class CreateBookDto { 8 | @MinLength(1, { 9 | message: '名称不能为空', 10 | }) 11 | @MaxLength(BookConstants.MAX_TITLE_LENGTH, { 12 | message: '名称不能超过 $constraint1 个字符', 13 | }) 14 | @IsString() 15 | readonly name: string; 16 | 17 | @MinLength(1, { 18 | message: '摘要不能为空', 19 | }) 20 | @MaxLength(BookConstants.MAX_SUMMARY_LENGTH, { 21 | message: '摘要不能超过 $constraint1 个字符', 22 | }) 23 | @IsString() 24 | readonly summary: string; 25 | 26 | @IsEnum(ArticleContentType, { 27 | message: '无效的内容格式', 28 | }) 29 | readonly contentType: number; 30 | 31 | @IsUrl({ 32 | protocols: ['https'], 33 | require_protocol: true, 34 | }, { 35 | message: '封面图片不能为空', 36 | }) 37 | readonly coverURL: string; 38 | } -------------------------------------------------------------------------------- /src/book/dto/create-bookcategory.dto.ts: -------------------------------------------------------------------------------- 1 | import { 2 | IsString, 3 | MinLength, 4 | MaxLength, 5 | IsInt, 6 | } from 'class-validator'; 7 | import { CategoryConstants } from '../../constants/category'; 8 | import { BookConstants } from '../../constants/book'; 9 | 10 | export class CreateBookCategoryDto { 11 | @MinLength(1, { 12 | message: '名称不能为空', 13 | }) 14 | @MaxLength(BookConstants.MAX_CATEGORY_TITLE_LENGTH, { 15 | message: '名称不能超过 $constraint1 个字符', 16 | }) 17 | @IsString() 18 | readonly name: string; 19 | 20 | @IsInt({ 21 | message: '无效的顺序', 22 | }) 23 | readonly sequence: number; 24 | 25 | @MinLength(1, { 26 | message: '路径不能为空', 27 | }) 28 | @MaxLength(CategoryConstants.MAX_PATHNAME_LENGTH, { 29 | message: '路径不能超过 $constraint1 个字符', 30 | }) 31 | @IsString() 32 | readonly pathname: string; 33 | } -------------------------------------------------------------------------------- /src/book/dto/create-handbook.dto.ts: -------------------------------------------------------------------------------- 1 | import { 2 | IsString, 3 | MinLength, 4 | MaxLength, 5 | } from 'class-validator'; 6 | import { HandBookConstants } from '../../constants/handbook'; 7 | 8 | export class CreateHandBookDto { 9 | @MinLength(HandBookConstants.TITLE_MIN_LENGTH, { 10 | message: '小册标题不能为空', 11 | }) 12 | @MaxLength(HandBookConstants.TITLE_MAX_LENGTH, { 13 | message: '小册标题不能超过 $constraint1 个字符', 14 | }) 15 | @IsString({ 16 | message: '小册标题不能为空', 17 | }) 18 | readonly name: string; 19 | } -------------------------------------------------------------------------------- /src/book/dto/update-book.dto.ts: -------------------------------------------------------------------------------- 1 | import { CreateBookDto } from './create-book.dto'; 2 | 3 | export class UpdateBookDto extends CreateBookDto { 4 | 5 | } -------------------------------------------------------------------------------- /src/book/dto/update-category.dto.ts: -------------------------------------------------------------------------------- 1 | import { 2 | IsInt, 3 | } from 'class-validator'; 4 | import { CreateBookCategoryDto } from './create-bookcategory.dto'; 5 | 6 | export class UpdateBookCategoryDto extends CreateBookCategoryDto { 7 | @IsInt({ 8 | message: '无效的id', 9 | }) 10 | readonly id: number; 11 | } -------------------------------------------------------------------------------- /src/book/dto/update-handbook-chapter.dto.ts: -------------------------------------------------------------------------------- 1 | import { 2 | MinLength, MaxLength, IsString, IsBoolean, 3 | } from 'class-validator'; 4 | import { HandBookConstants } from '../../constants/handbook'; 5 | 6 | export class UpdateHandBookChapterDto { 7 | @MinLength(1, { 8 | message: '章节标题不能为空', 9 | }) 10 | @MaxLength(HandBookConstants.CHAPTER_NAME_MAX_LENGTH, { 11 | message: '章节标题不能超过 $constraint1 个字符', 12 | }) 13 | @IsString({ 14 | message: '章节标题不能为空', 15 | }) 16 | readonly name: string; 17 | } 18 | 19 | export class UpdateHandBookChapterContentDto { 20 | @MaxLength(HandBookConstants.CHAPTER_CONTENT_MAX_LENGTH, { 21 | message: '章节内容不能超过 $constraint1 个字符', 22 | }) 23 | @IsString({ 24 | message: '无效的章节内容', 25 | }) 26 | readonly content: string; 27 | } 28 | 29 | export class UpdateHandBookChapterTryReadDto { 30 | @IsBoolean() 31 | readonly tryRead: boolean; 32 | } -------------------------------------------------------------------------------- /src/cms/dto/crawler-article.dto.ts: -------------------------------------------------------------------------------- 1 | import { 2 | IsInt, 3 | } from 'class-validator'; 4 | import { EditArticleDto } from './edit-article.dto'; 5 | 6 | export class CrawlerArticleDto extends EditArticleDto { 7 | @IsInt({ 8 | message: '无效的crawlerID', 9 | }) 10 | readonly crawlerID: number; 11 | } -------------------------------------------------------------------------------- /src/cms/dto/create-category.dto.ts: -------------------------------------------------------------------------------- 1 | import { 2 | IsString, 3 | MinLength, 4 | MaxLength, 5 | IsInt, 6 | } from 'class-validator'; 7 | import { CategoryConstants } from '../../constants/category'; 8 | 9 | export class CreateCategoryDto { 10 | @MinLength(1, { 11 | message: '名称不能为空', 12 | }) 13 | @MaxLength(CategoryConstants.MAX_TITLE_LENGTH, { 14 | message: '名称不能超过 $constraint1 个字符', 15 | }) 16 | @IsString() 17 | readonly name: string; 18 | 19 | @IsInt({ 20 | message: '无效的顺序', 21 | }) 22 | readonly sequence: number; 23 | 24 | @MinLength(1, { 25 | message: '路径不能为空', 26 | }) 27 | @MaxLength(CategoryConstants.MAX_PATHNAME_LENGTH, { 28 | message: '路径不能超过 $constraint1 个字符', 29 | }) 30 | @IsString() 31 | readonly pathname: string; 32 | } -------------------------------------------------------------------------------- /src/cms/dto/create-tag.dto.ts: -------------------------------------------------------------------------------- 1 | import { 2 | IsString, 3 | MinLength, 4 | MaxLength, 5 | IsUrl, 6 | ArrayMinSize, 7 | ArrayMaxSize, 8 | IsInt, 9 | IsArray, 10 | } from 'class-validator'; 11 | import { TagConstants } from '../../constants/tag'; 12 | 13 | export class CreateTagDto { 14 | @MinLength(TagConstants.MIN_TITLE_LENGTH, { 15 | message: '名称不能为空', 16 | }) 17 | @MaxLength(TagConstants.MAX_TITLE_LENGTH, { 18 | message: '名称不能超过 $constraint1 个字符', 19 | }) 20 | @IsString() 21 | readonly name: string; 22 | 23 | @IsUrl({ 24 | protocols: ['https'], 25 | require_protocol: true, 26 | }, { 27 | message: '图标不能为空', 28 | }) 29 | readonly iconURL: string; 30 | 31 | @ArrayMinSize(1, { 32 | message: '请选择分类', 33 | }) 34 | @ArrayMaxSize(TagConstants.MAX_CATEGORY_COUNT, { 35 | message: '最多只能选择 $constraint1 个分类', 36 | }) 37 | @IsArray({ 38 | message: '请选择分类', 39 | }) 40 | @IsInt({ 41 | each: true, 42 | message: '无效的分类', 43 | }) 44 | readonly categories: number[]; 45 | } -------------------------------------------------------------------------------- /src/cms/dto/switch-editor.dto.ts: -------------------------------------------------------------------------------- 1 | import { 2 | IsEnum, 3 | } from 'class-validator'; 4 | import { ArticleContentType } from '../../entity/article.entity'; 5 | import { CreateDraftDto } from './create-draft.dto'; 6 | 7 | export class SwitchEditorDto extends CreateDraftDto { 8 | @IsEnum(ArticleContentType, { 9 | message: '无效的编辑器', 10 | }) 11 | readonly editorType: number; 12 | } 13 | -------------------------------------------------------------------------------- /src/cms/dto/update-category.dto.ts: -------------------------------------------------------------------------------- 1 | import { 2 | IsInt, 3 | } from 'class-validator'; 4 | 5 | import { CreateCategoryDto } from './create-category.dto'; 6 | 7 | export class UpdateCategoryDto extends CreateCategoryDto { 8 | @IsInt({ 9 | message: '无效的id', 10 | }) 11 | readonly id: number; 12 | } -------------------------------------------------------------------------------- /src/cms/dto/update-tag.dto.ts: -------------------------------------------------------------------------------- 1 | import { 2 | IsInt, 3 | } from 'class-validator'; 4 | import { CreateTagDto } from './create-tag.dto'; 5 | 6 | export class UpdateTagDto extends CreateTagDto { 7 | @IsInt({ 8 | message: '无效的id', 9 | }) 10 | readonly id: number; 11 | } -------------------------------------------------------------------------------- /src/cms/recommend.controller.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Controller, Get, Res, Param, 3 | } from '@nestjs/common'; 4 | import { UserService } from '../user/user.service'; 5 | import { CategoryService } from './category.service'; 6 | import { MyHttpException } from '../core/exception/my-http.exception'; 7 | import { ErrorCode } from '../constants/error'; 8 | 9 | @Controller() 10 | export class RecommendController { 11 | constructor( 12 | private readonly categoryService: CategoryService, 13 | ) {} 14 | 15 | @Get('/recommendations/authors/:categoryPathName') 16 | async usersView(@Param('categoryPathName') categoryPathName: string, @Res() res) { 17 | const [categories] = await Promise.all([ 18 | this.categoryService.all(), 19 | ]); 20 | const category = categories.find(c => c.pathname === categoryPathName); 21 | if (!category && categoryPathName !== 'recommended') { 22 | throw new MyHttpException({ 23 | errorCode: ErrorCode.NotFound.CODE, 24 | }); 25 | } 26 | res.render('pages/recommendations/authors', { 27 | categoryPathName, 28 | categories, 29 | }); 30 | } 31 | } -------------------------------------------------------------------------------- /src/common/common.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { OSSService } from './oss.service'; 3 | import { CommonController } from './common.controller'; 4 | import { TypeOrmModule } from '@nestjs/typeorm'; 5 | import { Image } from '../entity/image.entity'; 6 | import { MyLoggerService } from './logger.service'; 7 | 8 | @Module({ 9 | imports: [ 10 | TypeOrmModule.forFeature([ 11 | Image, 12 | ]), 13 | ], 14 | controllers: [ 15 | CommonController, 16 | ], 17 | providers: [ 18 | MyLoggerService, 19 | OSSService, 20 | ], 21 | exports: [ 22 | MyLoggerService, 23 | OSSService, 24 | ], 25 | }) 26 | export class CommonModule {} -------------------------------------------------------------------------------- /src/common/logger.service.ts: -------------------------------------------------------------------------------- 1 | import * as pino from 'pino'; 2 | import * as moment from 'moment'; 3 | 4 | const logger = pino(); 5 | 6 | class LogData { 7 | public message?: string; 8 | public data?: any; 9 | } 10 | 11 | export class MyLoggerService { 12 | 13 | private writeLog(logMethod: string, logData: LogData) { 14 | logData = logData || { message: '', data: {} }; 15 | (logData as any).timeLocal = moment().format('YYYY-MM-DD HH:mm:ss.SSS'); 16 | logger[logMethod](logData); 17 | } 18 | 19 | debug(logData: LogData) { 20 | this.writeLog('debug', logData); 21 | } 22 | 23 | info(logData: LogData) { 24 | this.writeLog('info', logData); 25 | } 26 | 27 | error(logData: LogData) { 28 | this.writeLog('error', logData); 29 | } 30 | 31 | warn(logData: LogData) { 32 | this.writeLog('warn', logData); 33 | } 34 | 35 | fatal(logData: LogData) { 36 | this.writeLog('fatal', logData); 37 | } 38 | } -------------------------------------------------------------------------------- /src/config/cfg.development.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | 3 | }; 4 | -------------------------------------------------------------------------------- /src/config/cfg.production.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | 3 | }; 4 | -------------------------------------------------------------------------------- /src/config/cfg.test.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | 3 | }; 4 | -------------------------------------------------------------------------------- /src/config/config.module.ts: -------------------------------------------------------------------------------- 1 | import { Global, Module } from '@nestjs/common'; 2 | import { ConfigService } from './config.service'; 3 | 4 | @Global() 5 | @Module({ 6 | providers: [ ConfigService ], 7 | exports: [ ConfigService ], 8 | }) 9 | export class ConfigModule {} -------------------------------------------------------------------------------- /src/config/type/AliyunOSSConfig.ts: -------------------------------------------------------------------------------- 1 | import BaseConfig from './BaseConfig'; 2 | 3 | export default class AliyunOSSConfig extends BaseConfig { 4 | readonly accessKeyID: string; 5 | readonly accessKeySecret: string; 6 | readonly bucket: string; 7 | readonly region: string; 8 | readonly uploadActionURL: string; 9 | readonly uploadPrefix: string; // 本地开发时,上传路径加个前缀 10 | readonly uploadFieldName: string; 11 | readonly expiration: number; // 设置Policy的失效时间, 单位小时 12 | readonly callbackSecretToken: string; 13 | 14 | constructor(cfg) { 15 | super(cfg); 16 | } 17 | } -------------------------------------------------------------------------------- /src/config/type/AliyunSMSConfig.ts: -------------------------------------------------------------------------------- 1 | import BaseConfig from './BaseConfig'; 2 | 3 | export default class AliyunSMSConfig extends BaseConfig { 4 | readonly accessKeyID: string; 5 | readonly accessKeySecret: string; 6 | readonly signName: string; 7 | readonly templateCode: string; 8 | 9 | constructor(cfg) { 10 | super(cfg); 11 | } 12 | } -------------------------------------------------------------------------------- /src/config/type/BaseConfig.ts: -------------------------------------------------------------------------------- 1 | export default class BaseConfig { 2 | constructor(cfg) { 3 | for (const key of Object.keys(cfg)) { 4 | this[key] = cfg[key]; 5 | } 6 | } 7 | } -------------------------------------------------------------------------------- /src/config/type/DBConfig.ts: -------------------------------------------------------------------------------- 1 | import BaseConfig from './BaseConfig'; 2 | import { Logger } from 'typeorm/logger/Logger'; 3 | import { LoggerOptions } from 'typeorm/logger/LoggerOptions'; 4 | 5 | export default class DBConfig extends BaseConfig { 6 | readonly type: 'mysql' | 'mariadb'; 7 | readonly host: string; 8 | readonly port: number; 9 | readonly username: string; 10 | readonly password: string; 11 | readonly database: string; 12 | readonly entities: string[]; 13 | readonly synchronize: boolean; 14 | readonly logging: LoggerOptions; 15 | readonly logger: 'advanced-console' | 'simple-console' | 'file' | 'debug' | Logger; 16 | // If query execution time exceed this given max execution time (in milliseconds) then logger will log this query. 17 | readonly maxQueryExecutionTime: number; 18 | 19 | constructor(cfg) { 20 | super(cfg); 21 | this.database = process.env.MILI_MYSQL_DB || this.database; 22 | this.username = process.env.MILI_MYSQL_USER || this.username; 23 | this.password = process.env.MILI_MYSQL_PASSWORD || this.password; 24 | } 25 | } -------------------------------------------------------------------------------- /src/config/type/GeetestCaptcha.ts: -------------------------------------------------------------------------------- 1 | import BaseConfig from './BaseConfig'; 2 | 3 | export default class GeetestCaptcha extends BaseConfig { 4 | readonly geetest_id: string; 5 | readonly geetest_key: string; 6 | readonly disabled: boolean; 7 | 8 | constructor(cfg) { 9 | super(cfg); 10 | this.disabled = !(this.geetest_id && this.geetest_key); 11 | } 12 | } -------------------------------------------------------------------------------- /src/config/type/GithubConfig.ts: -------------------------------------------------------------------------------- 1 | import BaseConfig from './BaseConfig'; 2 | 3 | export default class GithubConfig extends BaseConfig { 4 | readonly clientID: string; 5 | readonly clientSecret: string; 6 | readonly accessTokenURL: string; 7 | readonly userInfoURL: string; 8 | readonly authorizeURL: string; 9 | 10 | constructor(cfg) { 11 | super(cfg); 12 | } 13 | } -------------------------------------------------------------------------------- /src/config/type/RedisConfig.ts: -------------------------------------------------------------------------------- 1 | import BaseConfig from './BaseConfig'; 2 | 3 | export default class RedisConfig extends BaseConfig { 4 | readonly host: string; 5 | readonly port: number; 6 | readonly keyPrefix: string; 7 | 8 | constructor(cfg) { 9 | super(cfg); 10 | this.keyPrefix = process.env.MILI_REDIS_PREFIX || this.keyPrefix; 11 | } 12 | } -------------------------------------------------------------------------------- /src/config/type/ServerConfig.ts: -------------------------------------------------------------------------------- 1 | import BaseConfig from './BaseConfig'; 2 | 3 | export class ServerConfig extends BaseConfig { 4 | readonly siteName: string; 5 | readonly companyName: string; 6 | readonly icp: string; 7 | readonly url: string; 8 | readonly mURL: string; 9 | readonly domain: string; 10 | readonly mDomain: string; 11 | readonly allowOrigins: string[]; 12 | readonly port: number; 13 | readonly apiPrefix: string; 14 | readonly passSalt: string; 15 | readonly tokenName: string; 16 | readonly tokenSecret: string; 17 | readonly tokenMaxAge: number; 18 | readonly cookieSecret: string; 19 | readonly rateLimitWindowMs: number; // 时间窗口,单位毫秒 20 | readonly rateLimitMax: number; // limit each IP to rateLimitMax requests per windowMs 21 | readonly swaggerPrefix: string; 22 | readonly xiaoceEmail: string; 23 | 24 | constructor(cfg) { 25 | super(cfg); 26 | } 27 | } -------------------------------------------------------------------------------- /src/config/type/StaticConfig.ts: -------------------------------------------------------------------------------- 1 | import BaseConfig from './BaseConfig'; 2 | 3 | export default class StaticConfig extends BaseConfig { 4 | readonly staticURL: string; // 前端静态资源 5 | readonly cssPath: string; // css路径 6 | readonly jsPath: string; // js路径 7 | readonly imgPath: string; // 图片路径 8 | readonly fontPath: string; // 字体路径 9 | readonly uploadImgURL: string; // 用户上传的图片 10 | readonly imgFormat: string; 11 | readonly imgMaxSize: number; // 设置上传图片的大小限制, 单位M 12 | readonly imgMaxSizeError: number; // 图片大小超过限制时的提示 13 | readonly userLevelChapterURL: string; // 用户等级在《如何使用米粒社区》中的章节url 14 | 15 | constructor(cfg) { 16 | super(cfg); 17 | } 18 | } -------------------------------------------------------------------------------- /src/config/type/StatsDConfig.ts: -------------------------------------------------------------------------------- 1 | import BaseConfig from './BaseConfig'; 2 | 3 | export default class StatsDConfig extends BaseConfig { 4 | readonly host: string; 5 | readonly port: number; 6 | readonly prefix: string; 7 | readonly telegraf: boolean; 8 | readonly protocol: 'tcp' | 'udp' | 'uds'; 9 | 10 | constructor(cfg) { 11 | super(cfg); 12 | } 13 | } -------------------------------------------------------------------------------- /src/config/type/WeiboConfig.ts: -------------------------------------------------------------------------------- 1 | import BaseConfig from './BaseConfig'; 2 | 3 | export default class WeiboConfig extends BaseConfig { 4 | readonly appKey: string; 5 | readonly appSecret: string; 6 | readonly state: string; 7 | readonly redirectURL: string; 8 | readonly authorizeURL: string; 9 | readonly accessTokenURL: string; 10 | readonly userInfoURL: string; 11 | 12 | private serverURL: string; 13 | 14 | constructor(cfg) { 15 | super(cfg); 16 | this.redirectURL = this.serverURL + this.redirectURL; 17 | } 18 | } -------------------------------------------------------------------------------- /src/constants/article.ts: -------------------------------------------------------------------------------- 1 | export class ArticleConstants { 2 | static readonly SUMMARY_LENGTH: number = 100; 3 | static readonly MAX_CATEGORY_COUNT: number = 1; 4 | static readonly MAX_TAG_COUNT: number = 1; 5 | static readonly MAX_TITLE_LENGTH: number = 100; 6 | static readonly CONTENT_MAX_LENGTH = 65535; 7 | static readonly HTML_CONTENT_MAX_LENGTH = 65535; 8 | } 9 | 10 | export class CrawlerConstants { 11 | static readonly SELECTOR_MAX_LENGTH = 100; // 选择器最大长度 12 | 13 | static readonly SELECTOR_MIN_LENGTH = 1; // 选择器最小长度 14 | 15 | static readonly FROM_TEMPLATE_MAX_LENGTH = 1000; // 来源模板最大长度 16 | 17 | static readonly FROM_TEMPLATE_MIN_LENGTH = 1; // 来源模板最小长度 18 | } 19 | 20 | export class MarkedConstants { 21 | static readonly options = { 22 | gfm: true, 23 | breaks: true, 24 | }; 25 | } -------------------------------------------------------------------------------- /src/constants/boilingpoint.ts: -------------------------------------------------------------------------------- 1 | export class BoilingPointConstants { 2 | static readonly MAX_CONTENT_LENGTH: number = 1000; 3 | static readonly MAX_IMAGE_COUNT: number = 9; 4 | static readonly MAX_TOPIC_TITLE_LENGTH: number = 10; 5 | static readonly SUMMARY_LENGTH: number = 100; 6 | 7 | static readonly ReportReasons: number[] = [ 8 | 0, // 其它 9 | 1, // 和话题不符 10 | 2, // 恶意攻击谩骂 11 | 3, // 广告营销 12 | ]; 13 | } -------------------------------------------------------------------------------- /src/constants/book.ts: -------------------------------------------------------------------------------- 1 | export class BookConstants { 2 | static readonly MAX_STAR_CONTENT_LENGTH: number = 200; 3 | static readonly MIN_STAR_VALUE: number = 1; 4 | static readonly MAX_STAR_VALUE: number = 5; 5 | static readonly MAX_CATEGORY_TITLE_LENGTH: number = 20; 6 | 7 | static readonly MAX_TITLE_LENGTH: number = 50; 8 | static readonly MAX_SUMMARY_LENGTH: number = 100; 9 | 10 | static readonly MAX_CHAPTER_TITLE_LENGTH: number = 50; 11 | static readonly MAX_CHAPTER_CONTENT_LENGTH: number = 20000; 12 | } -------------------------------------------------------------------------------- /src/constants/category.ts: -------------------------------------------------------------------------------- 1 | export class CategoryConstants { 2 | static readonly MIN_TITLE_LENGTH: number = 1; 3 | static readonly MAX_TITLE_LENGTH: number = 20; 4 | static readonly MAX_PATHNAME_LENGTH: number = 100; 5 | } -------------------------------------------------------------------------------- /src/constants/comment.ts: -------------------------------------------------------------------------------- 1 | export class CommentConstants { 2 | static readonly MinContentLength: number = 1; 3 | static readonly MaxContentLength: number = 500; 4 | 5 | static readonly ArticleTable: string = 'articles'; 6 | static readonly BookChapterTable: string = 'book_chapters'; 7 | static readonly BoilingPointTable: string = 'boilingpoints'; 8 | 9 | static readonly BookChapterCollectionTable: string = 'books'; 10 | 11 | static readonly LikeArticleCommentTable: string = 'like_article_comments'; 12 | static readonly LikeBookChapterCommentTable: string = 'like_bookchapter_comments'; 13 | static readonly LikeBoilingPointCommentTable: string = 'like_boiling_comments'; 14 | 15 | static readonly ArticleCommentTable: string = 'article_comments'; 16 | static readonly BookChapterCommentTable: string = 'book_chapter_comments'; 17 | static readonly BoilingPointCommentTable: string = 'boilingpoint_comments'; 18 | 19 | static readonly SourceArticle = 'article'; 20 | static readonly SourceBoilingPoint = 'boilingpoint'; 21 | static readonly SourceBookChapter = 'bookchapter'; 22 | static readonly SourceHankBookChapter = 'handbookchapter'; 23 | } 24 | -------------------------------------------------------------------------------- /src/constants/exercise.ts: -------------------------------------------------------------------------------- 1 | export class ExerciseConstants { 2 | } 3 | 4 | export class QuestionConstants { 5 | // 题目最大长度 6 | static readonly TitleMaxLength: number = 200; 7 | 8 | // 代码段最大长度 9 | static readonly CodeSnippetMaxLength: number = 2000; 10 | 11 | // 选项最小值,从A开始 12 | static readonly OptionMinValue: number = 65; 13 | 14 | // 选项最大值 15 | static readonly OptionMaxValue: number = 74; 16 | 17 | // 答题解析最大长度 18 | static readonly AnalysisMaxLength: number = 2000; 19 | } -------------------------------------------------------------------------------- /src/constants/handbook.ts: -------------------------------------------------------------------------------- 1 | export class HandBookConstants { 2 | static readonly MAX_PRICE: number = 100000000; // 单位分 3 | 4 | static readonly TITLE_MIN_LENGTH: number = 1; 5 | static readonly TITLE_MAX_LENGTH: number = 50; 6 | 7 | static readonly INTRODUCE_MAX_LENGTH: number = 100000; 8 | 9 | static readonly SUMMARY_MIN_LENGTH: number = 1; 10 | static readonly SUMMARY_MAX_LENGTH: number = 100000; 11 | 12 | static readonly AUTHOR_INTRO_MAX_LENGTH: number = 100000; 13 | 14 | static readonly CHAPTER_NAME_MAX_LENGTH: number = 100; 15 | static readonly CHAPTER_CONTENT_MAX_LENGTH: number = 100000; 16 | } -------------------------------------------------------------------------------- /src/constants/social.ts: -------------------------------------------------------------------------------- 1 | export class SocialConstants { 2 | static readonly WEIBO: string = 'weibo'; 3 | static readonly QQ: string = 'qq'; 4 | static readonly WEIXIN: string = 'weixin'; 5 | } -------------------------------------------------------------------------------- /src/constants/tag.ts: -------------------------------------------------------------------------------- 1 | export class TagConstants { 2 | static readonly MIN_TITLE_LENGTH: number = 1; 3 | static readonly MAX_TITLE_LENGTH: number = 20; 4 | static readonly MAX_CATEGORY_COUNT: number = 5; 5 | } 6 | -------------------------------------------------------------------------------- /src/core/decorators/roles.decorator.ts: -------------------------------------------------------------------------------- 1 | import { SetMetadata } from '@nestjs/common'; 2 | 3 | export const Roles = (...roles: number[]) => SetMetadata('roles', roles); -------------------------------------------------------------------------------- /src/core/decorators/user.decorator.ts: -------------------------------------------------------------------------------- 1 | import { createParamDecorator } from '@nestjs/common'; 2 | 3 | export const CurUser = createParamDecorator((data, req) => { 4 | return req.user; 5 | }); -------------------------------------------------------------------------------- /src/core/exception/my-http.exception.ts: -------------------------------------------------------------------------------- 1 | import { HttpStatus, HttpException } from '@nestjs/common'; 2 | import { ErrorCode } from '../../constants/error'; 3 | 4 | class MyHttpExceptionData { 5 | errorCode?: number; 6 | message?: string; 7 | } 8 | 9 | export class MyHttpException extends HttpException { 10 | constructor(expData: MyHttpExceptionData) { 11 | if (typeof expData.errorCode === 'undefined') { 12 | expData.errorCode = ErrorCode.ParamsError.CODE; 13 | } 14 | super(expData, HttpStatus.OK); 15 | } 16 | } -------------------------------------------------------------------------------- /src/core/middleware/compression.middleware.ts: -------------------------------------------------------------------------------- 1 | import { Injectable, NestMiddleware } from '@nestjs/common'; 2 | import * as compression from 'compression'; 3 | import { MyLoggerService } from '../../common/logger.service'; 4 | import { ConfigService } from '../../config/config.service'; 5 | 6 | @Injectable() 7 | export class CompressionMiddleware implements NestMiddleware { 8 | private compression: any; 9 | 10 | constructor( 11 | private readonly configService: ConfigService, 12 | private readonly logger: MyLoggerService, 13 | ) { 14 | this.compression = compression(); 15 | } 16 | 17 | use(request: Request, response: Response, next: () => void) { 18 | const req: any = request; 19 | const res: any = response; 20 | 21 | this.compression(req, res, next); 22 | } 23 | } -------------------------------------------------------------------------------- /src/core/middleware/cookie-parser.middleware.ts: -------------------------------------------------------------------------------- 1 | import { Injectable, NestMiddleware } from '@nestjs/common'; 2 | import * as cookieParser from 'cookie-parser'; 3 | 4 | import { MyLoggerService } from '../../common/logger.service'; 5 | import { ConfigService } from '../../config/config.service'; 6 | 7 | @Injectable() 8 | export class CookieParserMiddleware implements NestMiddleware { 9 | private cookieParser: any; 10 | 11 | constructor( 12 | private readonly configService: ConfigService, 13 | private readonly logger: MyLoggerService, 14 | ) { 15 | this.cookieParser = cookieParser(this.configService.server.cookieSecret); 16 | } 17 | 18 | use(request: Request, response: Response, next: () => void) { 19 | const req: any = request; 20 | const res: any = response; 21 | 22 | this.cookieParser(req, res, next); 23 | } 24 | } -------------------------------------------------------------------------------- /src/core/middleware/cors.middleware.ts: -------------------------------------------------------------------------------- 1 | import { Injectable, NestMiddleware } from '@nestjs/common'; 2 | import { ConfigService } from '../../config/config.service'; 3 | import { MyRequest } from '../types/net'; 4 | import { MyLoggerService } from '../../common/logger.service'; 5 | 6 | @Injectable() 7 | export class CorsMiddleware implements NestMiddleware { 8 | constructor( 9 | private readonly configService: ConfigService, 10 | private readonly logger: MyLoggerService, 11 | ) {} 12 | 13 | use(request: Request, response: Response, next: () => void) { 14 | const req: any = request; 15 | const res: any = response; 16 | 17 | if (this.configService.server.allowOrigins.indexOf(req.headers.origin) >= 0) { 18 | res.header('Access-Control-Allow-Origin', req.headers.origin); 19 | } 20 | res.header('Access-Control-Allow-Methods', 'OPTIONS,HEAD,PUT,POST,GET,DELETE'); 21 | next(); 22 | } 23 | } -------------------------------------------------------------------------------- /src/core/middleware/csrf.middleware.ts: -------------------------------------------------------------------------------- 1 | import * as url from 'url'; 2 | import * as csurf from 'csurf'; 3 | import { Injectable, NestMiddleware } from '@nestjs/common'; 4 | import { APIPrefix } from '../../constants/constants'; 5 | import { MyLoggerService } from '../../common/logger.service'; 6 | import { MyRequest } from '../types/net'; 7 | 8 | const csrfProtection = csurf({ 9 | cookie: true, 10 | ignoreMethods: ['GET', 'HEAD', 'OPTIONS'], 11 | }); 12 | 13 | @Injectable() 14 | export class CSRFMiddleware implements NestMiddleware { 15 | constructor( 16 | private readonly logger: MyLoggerService, 17 | ) {} 18 | 19 | use(request: Request, response: Response, next: () => void) { 20 | const req: any = request; 21 | const res: any = response; 22 | 23 | const pathname = url.parse(req.originalUrl).pathname; 24 | const trustArr = [ 25 | `${APIPrefix}/common/oss/callback`, 26 | ]; 27 | if (trustArr.indexOf(pathname) >= 0) { 28 | next(); 29 | return; 30 | } 31 | csrfProtection(req, res, next); 32 | } 33 | } -------------------------------------------------------------------------------- /src/core/middleware/helmet.middleware.ts: -------------------------------------------------------------------------------- 1 | import { Injectable, NestMiddleware } from '@nestjs/common'; 2 | import * as helmet from 'helmet'; 3 | import { MyLoggerService } from '../../common/logger.service'; 4 | import { ConfigService } from '../../config/config.service'; 5 | 6 | @Injectable() 7 | export class HelmetMiddleware implements NestMiddleware { 8 | private helmet: any; 9 | 10 | constructor( 11 | private readonly configService: ConfigService, 12 | private readonly logger: MyLoggerService, 13 | ) { 14 | this.helmet = helmet(); 15 | } 16 | 17 | use(request: Request, response: Response, next: () => void) { 18 | const req: any = request; 19 | const res: any = response; 20 | 21 | this.helmet(req, res, next); 22 | } 23 | } -------------------------------------------------------------------------------- /src/core/middleware/ip.middleware.ts: -------------------------------------------------------------------------------- 1 | import * as requestIp from 'request-ip'; 2 | import { Injectable, NestMiddleware } from '@nestjs/common'; 3 | import { MyLoggerService } from '../../common/logger.service'; 4 | import { MyRequest } from '../types/net'; 5 | 6 | @Injectable() 7 | export class IpMiddleware implements NestMiddleware { 8 | constructor( 9 | private readonly logger: MyLoggerService, 10 | ) {} 11 | 12 | use(request: Request, response: Response, next: () => void) { 13 | const req: MyRequest = request as any; 14 | req.reqStartTime = Date.now(); 15 | 16 | const clientIp = requestIp.getClientIp(request as any); 17 | (request as any).clientIp = clientIp; 18 | 19 | this.logger.info({ 20 | data: { 21 | middleware: 'IpMiddleware', 22 | ip: clientIp, 23 | req: { 24 | method: 'GET', 25 | url: req.originalUrl, 26 | headers: { 27 | 'user-agent': req.headers['user-agent'], 28 | }, 29 | }, 30 | }, 31 | }); 32 | next(); 33 | } 34 | } -------------------------------------------------------------------------------- /src/core/middleware/rate-limit.middleware.ts: -------------------------------------------------------------------------------- 1 | import { Injectable, NestMiddleware } from '@nestjs/common'; 2 | import * as rateLimit from 'express-rate-limit'; 3 | import { MyLoggerService } from '../../common/logger.service'; 4 | import { ConfigService } from '../../config/config.service'; 5 | 6 | @Injectable() 7 | export class RateLimitMiddleware implements NestMiddleware { 8 | private rateLimit: any; 9 | 10 | constructor( 11 | private readonly configService: ConfigService, 12 | private readonly logger: MyLoggerService, 13 | ) { 14 | this.rateLimit = rateLimit({ 15 | windowMs: this.configService.server.rateLimitWindowMs, 16 | max: this.configService.server.rateLimitMax, 17 | }); 18 | } 19 | 20 | use(request: Request, response: Response, next: () => void) { 21 | const req: any = request; 22 | const res: any = response; 23 | 24 | this.rateLimit(req, res, next); 25 | } 26 | } -------------------------------------------------------------------------------- /src/core/pipes/parse-page.pipe.ts: -------------------------------------------------------------------------------- 1 | import { 2 | PipeTransform, 3 | Injectable, 4 | ArgumentMetadata, 5 | } from '@nestjs/common'; 6 | 7 | @Injectable() 8 | export class ParsePagePipe implements PipeTransform { 9 | constructor() {} 10 | 11 | transform(value: string, metadata: ArgumentMetadata): any { 12 | if (metadata.type !== 'query') { 13 | return value; 14 | } 15 | let page: number = parseInt(value, 10); 16 | if (isNaN(page)) { 17 | page = 1; 18 | } 19 | return page; 20 | } 21 | } -------------------------------------------------------------------------------- /src/core/pipes/should-int.pipe.ts: -------------------------------------------------------------------------------- 1 | import { 2 | PipeTransform, 3 | Injectable, 4 | ArgumentMetadata, 5 | } from '@nestjs/common'; 6 | 7 | @Injectable() 8 | export class ShouldIntPipe implements PipeTransform { 9 | transform(value: string, metadata: ArgumentMetadata): any { 10 | if (metadata.type !== 'param' && metadata.type !== 'query') { 11 | return value; 12 | } 13 | const val = parseInt(value, 10); 14 | if (isNaN(val)) { 15 | return undefined; 16 | } 17 | return val; 18 | } 19 | } -------------------------------------------------------------------------------- /src/core/types/net.ts: -------------------------------------------------------------------------------- 1 | export abstract class MyRequest { 2 | public reqStartTime: number; 3 | public readonly originalUrl: string; 4 | public readonly method: string; 5 | public readonly headers: any; 6 | public readonly clientIp: string; 7 | 8 | public abstract csrfToken(): string; 9 | } 10 | 11 | export class MyResponse { 12 | public locals: any; 13 | } -------------------------------------------------------------------------------- /src/dbscript/addColumn.ts: -------------------------------------------------------------------------------- 1 | export const addColumn = async (connection) => { 2 | await connection.manager.query('alter table handbook_chapters add column content text'); 3 | }; -------------------------------------------------------------------------------- /src/dbscript/renameTable.ts: -------------------------------------------------------------------------------- 1 | export const renameTable = async (connection) => { 2 | await connection.manager.query('rename table user_like_articles to like_articles'); 3 | }; -------------------------------------------------------------------------------- /src/dbscript/run.ts: -------------------------------------------------------------------------------- 1 | import { createConnection } from 'typeorm'; 2 | import { ConfigService } from '../config/config.service'; 3 | import { addColumn } from './addColumn'; 4 | 5 | const config = new ConfigService(); 6 | 7 | (async function run() { 8 | const connection = await createConnection(config.db); 9 | await addColumn(connection); 10 | 11 | // tslint:disable-next-line: no-console 12 | console.log('----------------- all done -----------------'); 13 | process.exit(0); 14 | }()); -------------------------------------------------------------------------------- /src/dbscript/updateUserArticleCount.ts: -------------------------------------------------------------------------------- 1 | import { parseCountResult } from '../utils/query'; 2 | 3 | export const updateUserArticleCount = async (connection) => { 4 | try { 5 | const users = await connection.manager.query(`SELECT id FROM users`); 6 | // tslint:disable-next-line: prefer-for-of 7 | for (let i = 0; i < users.length; i++) { 8 | const user = users[i]; 9 | let articleCount = await connection.manager.query(`SELECT COUNT(*) as count FROM articles WHERE user_id = ? 10 | AND deleted_at IS NULL`, [user.id]); 11 | 12 | articleCount = parseCountResult(articleCount); 13 | 14 | await connection.manager.query(`UPDATE users SET article_count = ? WHERE id = ?`, [articleCount, user.id]); 15 | } 16 | } catch (error) { 17 | process.exit(-1); 18 | } 19 | }; -------------------------------------------------------------------------------- /src/dbscript/updateUserArticleViewCount.ts: -------------------------------------------------------------------------------- 1 | import { parseCountResult } from '../utils/query'; 2 | 3 | export const updateUserArticleViewCount = async (connection) => { 4 | try { 5 | const users = await connection.manager.query(`SELECT id FROM users`); 6 | // tslint:disable-next-line: prefer-for-of 7 | for (let i = 0; i < users.length; i++) { 8 | const user = users[i]; 9 | let count = await connection.manager.query(`SELECT SUM(browse_count) as count FROM articles WHERE user_id = ? 10 | AND deleted_at IS NULL`, [user.id]); 11 | 12 | count = parseCountResult(count); 13 | 14 | await connection.manager.query(`UPDATE users SET article_view_count = ? WHERE id = ?`, [count, user.id]); 15 | } 16 | } catch (error) { 17 | process.exit(-1); 18 | } 19 | }; -------------------------------------------------------------------------------- /src/entity/category.entity.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Entity, 3 | Column, 4 | PrimaryGeneratedColumn, 5 | ManyToMany, 6 | } from 'typeorm'; 7 | import { User } from './user.entity'; 8 | 9 | @Entity({name: 'categories'}) 10 | export class Category { 11 | @PrimaryGeneratedColumn() 12 | id: number; 13 | 14 | @Column('datetime', { name: 'created_at' }) 15 | createdAt: Date; 16 | 17 | @Column('datetime', { name: 'updated_at' }) 18 | updatedAt: Date; 19 | 20 | @Column('datetime', { name: 'deleted_at', nullable: true, default: null }) 21 | deletedAt: Date; 22 | 23 | @Column('varchar', { length: 200 }) 24 | name: string; 25 | 26 | @Column('int') 27 | sequence: number; 28 | 29 | @Column('int', { name: 'parent_id' }) 30 | parentID: number; 31 | 32 | @Column('int', { name: 'follower_count' }) 33 | followerCount: number; // 有多少人关注 34 | 35 | @ManyToMany(type => User, user => user.followedCollections) 36 | followers: User[]; 37 | 38 | @Column('int', { name: 'article_count' }) 39 | articleCount: number; // 有多少人关注 40 | 41 | @Column('varchar', { length: 50 }) 42 | pathname: string; 43 | } -------------------------------------------------------------------------------- /src/entity/handbook.category.entity.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Entity, 3 | Column, 4 | PrimaryGeneratedColumn, 5 | } from 'typeorm'; 6 | 7 | @Entity({name: 'handbook_categories'}) 8 | export class HandbookCategory { 9 | @PrimaryGeneratedColumn() 10 | id: number; 11 | 12 | @Column('datetime', { name: 'created_at' }) 13 | createdAt: Date; 14 | 15 | @Column('datetime', { name: 'updated_at' }) 16 | updatedAt: Date; 17 | 18 | @Column('datetime', { name: 'deleted_at', nullable: true, default: null }) 19 | deletedAt: Date; 20 | 21 | @Column('varchar', { length: 200 }) 22 | name: string; 23 | 24 | @Column('int') 25 | sequence: number; 26 | } -------------------------------------------------------------------------------- /src/entity/image.entity.ts: -------------------------------------------------------------------------------- 1 | import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm'; 2 | 3 | // @Entity({name: 'images3'}) 4 | // export class Image2 { 5 | // @PrimaryGeneratedColumn() 6 | // id: number; 7 | 8 | // @Column('varchar', { length: 10 }) 9 | // name: string; 10 | // } 11 | 12 | @Entity({name: 'images'}) 13 | export class Image { 14 | @PrimaryGeneratedColumn() 15 | id: number; 16 | 17 | @Column('int', { name: 'width' }) 18 | width: number; 19 | 20 | @Column('int', { name: 'height' }) 21 | height: number; 22 | 23 | @Column('varchar', { name: 'url', length: 200 }) 24 | url: string; 25 | 26 | @Column('varchar', { name: 'mime', length: 50 }) 27 | mime: string; 28 | 29 | @Column('int', { name: 'size' }) 30 | size: number; // 单位字节 31 | 32 | @Column('varchar', { name: 'format', length: 50 }) 33 | format: string; 34 | } -------------------------------------------------------------------------------- /src/entity/listresult.entity.ts: -------------------------------------------------------------------------------- 1 | export class ListResult { 2 | list: T[]; 3 | count: number; 4 | page: number; 5 | pageSize: number; 6 | } 7 | -------------------------------------------------------------------------------- /src/entity/question.option.entity.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Entity, 3 | PrimaryGeneratedColumn, 4 | Column, 5 | ManyToOne, 6 | JoinColumn, 7 | } from 'typeorm'; 8 | import { Question } from './question.entity'; 9 | 10 | enum QuestionAnswer { 11 | A = 'A', 12 | B = 'B', 13 | C = 'C', 14 | D = 'D', 15 | E = 'E', 16 | F = 'F', 17 | G = 'G', 18 | H = 'H', 19 | I = 'I', 20 | J = 'J', 21 | K = 'K', 22 | } 23 | 24 | @Entity({name: 'question_options'}) 25 | export class QuestionOption { 26 | @PrimaryGeneratedColumn() 27 | id: number; 28 | 29 | @Column('char', { name: 'option_value', length: 1 }) 30 | optionValue: QuestionAnswer; 31 | 32 | @Column('varchar', { name: 'option_desc', length: 200 }) 33 | optionDesc: string; 34 | 35 | @Column('int', { name: 'question_id' }) 36 | questionID: number; 37 | 38 | @ManyToOne(type => Question, question => question.options) 39 | @JoinColumn({name: 'question_id'}) 40 | question: Question; 41 | } -------------------------------------------------------------------------------- /src/entity/settings.entity.ts: -------------------------------------------------------------------------------- 1 | import { Entity, Column, PrimaryGeneratedColumn, Unique, Index } from 'typeorm'; 2 | import { ArticleContentType } from './article.entity'; 3 | 4 | @Entity({name: 'settings'}) 5 | export class Settings { 6 | @PrimaryGeneratedColumn() 7 | id: number; 8 | 9 | @Column('int', { name: 'editor_type' }) 10 | editorType: ArticleContentType; 11 | 12 | @Index({ unique: true }) 13 | @Column('int', { name: 'user_id' }) 14 | userID: number; 15 | } -------------------------------------------------------------------------------- /src/entity/timeline.entity.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Entity, 3 | Column, 4 | PrimaryGeneratedColumn, 5 | } from 'typeorm'; 6 | 7 | export enum TimelineType { 8 | PublishComment = 'publish_comment', // 发表了评论,对评论进行评论时,@原评论者 9 | PublishArticle = 'publish_article', // 发表了文章 10 | UpComment = 'up_comment', // 赞了评论 11 | LinkArticle = 'link_article', // 喜欢了文章 12 | FollowUser = 'follow_user', // 关注了作者 13 | Join = 'join', // 加入,即用户注册了 14 | } 15 | 16 | @Entity({name: 'articles'}) 17 | export class Timeline { 18 | @PrimaryGeneratedColumn() 19 | id: number; 20 | 21 | @Column('datetime', { name: 'date' }) 22 | date: Date; 23 | 24 | @Column('varchar', { length: 200 }) 25 | username: string; 26 | 27 | @Column('varchar', { length: 30 }) 28 | type: TimelineType; 29 | 30 | @Column('varchar', { length: 200 }) 31 | articleTitle: string; 32 | 33 | } -------------------------------------------------------------------------------- /src/exercise/exercise.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { TypeOrmModule } from '@nestjs/typeorm'; 3 | import { Question } from '../entity/question.entity'; 4 | import { QuestionOption } from '../entity/question.option.entity'; 5 | import { QuestionController } from './question.controller'; 6 | import { QuestionService } from './question.service'; 7 | 8 | @Module({ 9 | imports: [ 10 | TypeOrmModule.forFeature([ 11 | Question, 12 | QuestionOption, 13 | ]), 14 | ], 15 | controllers: [ 16 | QuestionController, 17 | ], 18 | providers: [ 19 | QuestionService, 20 | ], 21 | }) 22 | export class ExerciseModule {} -------------------------------------------------------------------------------- /src/main.hmr.ts: -------------------------------------------------------------------------------- 1 | import { NestFactory } from '@nestjs/core'; 2 | import { AppModule } from './app.module'; 3 | import bootstrap from './bootstrap'; 4 | 5 | declare const module: any; 6 | 7 | async function main() { 8 | const app = await NestFactory.create(AppModule, { 9 | logger: false, 10 | }); 11 | await bootstrap(app); 12 | 13 | if (module.hot) { 14 | module.hot.accept(); 15 | module.hot.dispose(() => app.close()); 16 | } 17 | } 18 | 19 | main(); -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import { NestFactory } from '@nestjs/core'; 2 | import { AppModule } from './app.module'; 3 | import bootstrap from './bootstrap'; 4 | import { Logger } from '@nestjs/common'; 5 | 6 | async function main() { 7 | const app = await NestFactory.create(AppModule, { 8 | logger: process.env.NODE_ENV === 'development' ? new Logger() : false, 9 | }); 10 | await bootstrap(app); 11 | } 12 | 13 | main(); -------------------------------------------------------------------------------- /src/redis/redis.module.ts: -------------------------------------------------------------------------------- 1 | import { Global, Module, DynamicModule } from '@nestjs/common'; 2 | import { RedisService } from './redis.service'; 3 | import { ConfigService } from '../config/config.service'; 4 | 5 | @Global() 6 | @Module({}) 7 | export class RedisModule { 8 | static forRootAsync(options): DynamicModule { 9 | const providers = [ 10 | { 11 | provide: 'RedisModuleOptions', 12 | useFactory: options.useFactory, 13 | inject: options.inject, 14 | }, 15 | { 16 | provide: RedisService, 17 | useFactory: (configService: ConfigService) => { 18 | return new RedisService(configService); 19 | }, 20 | inject: ['RedisModuleOptions'], 21 | }, 22 | ]; 23 | 24 | return { 25 | module: RedisModule, 26 | providers, 27 | exports: providers, 28 | }; 29 | } 30 | } -------------------------------------------------------------------------------- /src/stats/stats.controller.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'lodash'; 2 | import { 3 | Controller, Req, Query, Get, 4 | } from '@nestjs/common'; 5 | import { StatsService } from './stats.service'; 6 | 7 | @Controller('stats') 8 | export class StatsController { 9 | 10 | constructor( 11 | private readonly statsService: StatsService, 12 | ) {} 13 | 14 | @Get('usertrack') 15 | async userTrack(@Req() req, @Query() query: any) { 16 | const data = _.pick(query, ['platform', 'clientID', 'osName', 'osVersion', 17 | 'language', 'country', 'deviceModel', 'deviceWidth', 'deviceHeight', 18 | 'referrer', 'url', 'browserName', 'browserVersion', 'ip', 'ua', 19 | ]); 20 | data.ip = req.ip; 21 | await this.statsService.userTrack(data); 22 | return {}; 23 | } 24 | } -------------------------------------------------------------------------------- /src/stats/stats.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { TypeOrmModule } from '@nestjs/typeorm'; 3 | import { UserTrack } from '../entity/stats.entity'; 4 | import { StatsController } from './stats.controller'; 5 | import { StatsService } from './stats.service'; 6 | import { CodeStatsController } from './codestats.controller'; 7 | 8 | @Module({ 9 | imports: [TypeOrmModule.forFeature([UserTrack])], 10 | controllers: [ 11 | CodeStatsController, 12 | StatsController, 13 | ], 14 | providers: [StatsService], 15 | }) 16 | export class StatsModule {} -------------------------------------------------------------------------------- /src/stats/stats.service.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'lodash'; 2 | import { Injectable } from '@nestjs/common'; 3 | import { Repository } from 'typeorm'; 4 | import { InjectRepository } from '@nestjs/typeorm'; 5 | import { UserTrack } from '../entity/stats.entity'; 6 | import { MyHttpException } from '../core/exception/my-http.exception'; 7 | import { ErrorCode } from '../constants/error'; 8 | 9 | @Injectable() 10 | export class StatsService { 11 | constructor( 12 | @InjectRepository(UserTrack) 13 | private readonly userTrackRepository: Repository, 14 | ) {} 15 | 16 | async userTrack(data: any) { 17 | if (!data.clientID) { 18 | throw new MyHttpException({ 19 | errorCode: ErrorCode.ParamsError.CODE, 20 | }); 21 | } 22 | const userTrack: UserTrack = new UserTrack(); 23 | data.date = new Date(); 24 | for (const key of Object.keys(data)) { 25 | userTrack[key] = data[key]; 26 | } 27 | return await this.userTrackRepository.save(userTrack); 28 | } 29 | } -------------------------------------------------------------------------------- /src/user/dto/update-password.dto.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Length, IsString, 3 | } from 'class-validator'; 4 | import { UserConstants } from '../../constants/constants'; 5 | import { ErrorCode } from '../../constants/error'; 6 | 7 | export class UpdatePasswordDto { 8 | @Length(UserConstants.PASSWORD_MIN_LENGTH, UserConstants.PASSWORD_MAX_LENGTH, { 9 | message: ErrorCode.InvalidPassword.MESSAGE, 10 | context: { 11 | errorCode: ErrorCode.InvalidPassword.CODE, 12 | }, 13 | }) 14 | @IsString({ 15 | message: ErrorCode.InvalidPassword.MESSAGE, 16 | context: { 17 | errorCode: ErrorCode.InvalidPassword.CODE, 18 | }, 19 | }) 20 | readonly oldPass: string; 21 | 22 | @Length(UserConstants.PASSWORD_MIN_LENGTH, UserConstants.PASSWORD_MAX_LENGTH, { 23 | message: ErrorCode.InvalidPassword.MESSAGE, 24 | context: { 25 | errorCode: ErrorCode.InvalidPassword.CODE, 26 | }, 27 | }) 28 | @IsString({ 29 | message: ErrorCode.InvalidPassword.MESSAGE, 30 | context: { 31 | errorCode: ErrorCode.InvalidPassword.CODE, 32 | }, 33 | }) 34 | readonly pass: string; 35 | } -------------------------------------------------------------------------------- /src/user/dto/update-userstatus.dto.ts: -------------------------------------------------------------------------------- 1 | import { 2 | IsInt, 3 | IsEnum, 4 | } from 'class-validator'; 5 | import { UserStatus } from '../../entity/user.entity'; 6 | 7 | export class UpdateUserStatusDto { 8 | @IsInt({ 9 | message: '无效的userID', 10 | }) 11 | readonly userID: number; 12 | 13 | @IsEnum(UserStatus, { 14 | message: '无效的用户状态', 15 | }) 16 | readonly status: number; 17 | } -------------------------------------------------------------------------------- /src/user/user.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { TypeOrmModule } from '@nestjs/typeorm'; 3 | import { UserController } from './user.controller'; 4 | import { UserService } from './user.service'; 5 | import { User } from '../entity/user.entity'; 6 | import { AdminController } from './admin.controller'; 7 | import { Settings } from '../entity/settings.entity'; 8 | import { CommonModule } from '../common/common.module'; 9 | 10 | @Module({ 11 | imports: [ 12 | TypeOrmModule.forFeature([ 13 | User, 14 | Settings, 15 | ]), 16 | CommonModule, 17 | ], 18 | controllers: [ 19 | UserController, 20 | AdminController, 21 | ], 22 | providers: [UserService], 23 | exports: [UserService], 24 | }) 25 | export class UserModule {} -------------------------------------------------------------------------------- /src/utils/query.ts: -------------------------------------------------------------------------------- 1 | export const parseCountResult = (countResult): number => { 2 | const count = countResult && countResult.length && countResult[0] && countResult[0].count || 0; 3 | return parseInt(count, 10); 4 | }; -------------------------------------------------------------------------------- /src/utils/social.ts: -------------------------------------------------------------------------------- 1 | export const getShareURL = (data): string => { 2 | const title = encodeURIComponent(data.title || ''); 3 | const imageURL = data.imageURL || ''; 4 | return `/share?platform=${data.platform}&title=${title}&imgurl=${imageURL}`; 5 | }; -------------------------------------------------------------------------------- /test/jest-e2e.json: -------------------------------------------------------------------------------- 1 | { 2 | "moduleFileExtensions": ["js", "json", "ts"], 3 | "rootDir": "./e2e", 4 | "testEnvironment": "node", 5 | "testRegex": ".e2e-spec.ts$", 6 | "transform": { 7 | "^.+\\.(t|j)s$": "ts-jest" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "exclude": ["node_modules", "test", "dist", "**/*spec.ts"] 4 | } 5 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "alwaysStrict": true, 4 | "declaration": true, 5 | "emitDecoratorMetadata": true, 6 | "experimentalDecorators": true, 7 | "incremental": true, 8 | "module": "commonjs", 9 | "outDir": "./dist/src", 10 | "removeComments": true, 11 | "sourceMap": true, 12 | "target": "es6" 13 | }, 14 | "include": [ 15 | "src/**/*" 16 | ], 17 | "exclude": ["node_modules"] 18 | } 19 | -------------------------------------------------------------------------------- /views/component/cms/pagination.njk: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /views/component/footer.njk: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /views/component/search/searchtype.njk: -------------------------------------------------------------------------------- 1 |
2 |
3 | 9 |
10 |
-------------------------------------------------------------------------------- /views/component/sidetool.njk: -------------------------------------------------------------------------------- 1 |
2 | 26 |
-------------------------------------------------------------------------------- /views/layout/admin.njk: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | {% block libcss %} 10 | {% endblock %} 11 | 12 | {% block css %} 13 | {% endblock %} 14 | 15 | 16 | 17 | {% from '../macros/js.njk' import loadjs %} 18 | 19 | {% block main %} 20 | {% endblock %} 21 | 22 | 26 | 27 | {% block pageData %} 28 | {% endblock %} 29 | 30 | {% block libjs %} 31 | {% endblock %} 32 | 33 | {% block devjs %} 34 | {% endblock %} 35 | 36 | {% block js %} 37 | {% endblock %} 38 | 39 | -------------------------------------------------------------------------------- /views/macros/js.njk: -------------------------------------------------------------------------------- 1 | {% macro loadjs(entry) %} 2 | {% if env === 'development' %} 3 | 4 | 5 | 6 | {% endif %} 7 | {% endmacro %} -------------------------------------------------------------------------------- /views/pages/admin/app.njk: -------------------------------------------------------------------------------- 1 | {% extends '../../layout/admin.njk' %} 2 | 3 | {% block libcss %} 4 | {% endblock %} 5 | 6 | {% block main %} 7 |
8 | {% endblock %} 9 | 10 | {% block pageData %} 11 | 16 | {% endblock %} 17 | 18 | {% block libjs %} 19 | {{ super() }} 20 | {% endblock %} 21 | 22 | {% block devjs %} 23 | {{ loadjs('entry/admin/app.js') }} 24 | {% endblock %} -------------------------------------------------------------------------------- /views/pages/books/chapter.njk: -------------------------------------------------------------------------------- 1 | {% extends '../../layout/empty.njk' %} 2 | 3 | {% block main %} 4 |
5 | {% endblock %} 6 | 7 | {% block footer %} 8 | {% endblock %} 9 | 10 | {% block pageData %} 11 | 19 | {% endblock %} 20 | 21 | {% block libcss %} 22 | {{ super() }} 23 | 24 | {% endblock %} 25 | 26 | {% block libjs %} 27 | {{ super() }} 28 | 29 | 30 | {% endblock %} 31 | 32 | {% block devjs %} 33 | {{ loadjs('entry/book/chapter.js') }} 34 | {% endblock %} -------------------------------------------------------------------------------- /views/pages/codeStats.njk: -------------------------------------------------------------------------------- 1 | {% extends '../layout/empty.njk' %} 2 | 3 | {% block main %} 4 |
5 | 10 |
11 |
12 | {% endblock %} 13 | 14 | {% block pageData %} 15 | 17 | {% endblock %} 18 | 19 | {% block devjs %} 20 | {{ loadjs('entry/codeStats.js') }} 21 | {% endblock %} -------------------------------------------------------------------------------- /views/pages/editor/drafts.njk: -------------------------------------------------------------------------------- 1 | {% extends '../../layout/empty.njk' %} 2 | 3 | {% block main %} 4 |
5 | {% endblock %} 6 | 7 | {% block pageData %} 8 | 12 | {% endblock %} 13 | 14 | {% block devjs %} 15 | {{ loadjs('entry/editor/drafts.js') }} 16 | {% endblock %} -------------------------------------------------------------------------------- /views/pages/editor/editMarkdownArticle.njk: -------------------------------------------------------------------------------- 1 | {# 创建草稿、编辑草稿、编辑文章都是用的这个页面(markdown格式) #} 2 | 3 | {% extends '../../layout/empty.njk' %} 4 | 5 | {% block main %} 6 |
7 | {% endblock %} 8 | 9 | {% block pageData %} 10 | 25 | {% endblock %} 26 | 27 | {% block devjs %} 28 | {{ loadjs('entry/editor/editMarkdownDraft.js') }} 29 | {% endblock %} -------------------------------------------------------------------------------- /views/pages/editor/editRichArticle.njk: -------------------------------------------------------------------------------- 1 | {# 创建草稿、编辑草稿、编辑文章都是用的这个页面(富文本) #} 2 | 3 | {% extends '../../layout/empty.njk' %} 4 | 5 | {% block main %} 6 |
7 | {% endblock %} 8 | 9 | {% block pageData %} 10 | 25 | {% endblock %} 26 | 27 | {% block devjs %} 28 | {{ loadjs('entry/editor/editRichDraft.js') }} 29 | {% endblock %} -------------------------------------------------------------------------------- /views/pages/editor/editor.md.njk: -------------------------------------------------------------------------------- 1 | {# iframe加载此页面,页面里是markdown编辑器 #} 2 | 3 | {% extends '../../layout/empty.njk' %} 4 | 5 | {% block main %} 6 |
7 | 8 | {% endblock %} 9 | 10 | {% block pageData %} 11 | 14 | {% endblock %} 15 | 16 | {% block devjs %} 17 | {{ loadjs('entry/editor/editor.md.js') }} 18 | {% endblock %} -------------------------------------------------------------------------------- /views/pages/editor/published.njk: -------------------------------------------------------------------------------- 1 | {% extends '../../layout/default.njk' %} 2 | 3 | {% block main %} 4 |
5 |
6 | {% endblock %} 7 | 8 | {% block footer %} 9 | {% endblock %} 10 | 11 | {% block pageData %} 12 | 20 | {% endblock %} 21 | 22 | {% block devjs %} 23 | {{ loadjs('entry/editor/published.js') }} 24 | {% endblock %} -------------------------------------------------------------------------------- /views/pages/handbook/editHandbook.njk: -------------------------------------------------------------------------------- 1 | {% extends '../../layout/empty.njk' %} 2 | 3 | {% block main %} 4 |
5 |
6 | {% endblock %} 7 | 8 | {% block pageData %} 9 | 17 | {% endblock %} 18 | 19 | {% block devjs %} 20 | {{ loadjs('entry/handbook/editHandbook.js') }} 21 | {% endblock %} -------------------------------------------------------------------------------- /views/pages/messages.njk: -------------------------------------------------------------------------------- 1 | {% extends '../layout/default.njk' %} 2 | 3 | {% block main %} 4 |
5 | {% endblock %} 6 | 7 | {% block footer %} 8 | {% endblock %} 9 | 10 | {% block devjs %} 11 | {{ loadjs('entry/messages.js') }} 12 | {% endblock %} -------------------------------------------------------------------------------- /views/pages/recommendations/authors.njk: -------------------------------------------------------------------------------- 1 | {% extends '../../layout/default.njk' %} 2 | 3 | {% block main %} 4 |
5 |
6 | {% endblock %} 7 | 8 | {% block footer %} 9 | {% endblock %} 10 | 11 | {% block pageData %} 12 | 16 | {% endblock %} 17 | 18 | {% block devjs %} 19 | {{ loadjs('entry/recommendations/authors.js') }} 20 | {% endblock %} -------------------------------------------------------------------------------- /views/pages/settings/settings.njk: -------------------------------------------------------------------------------- 1 | {% extends '../../layout/default.njk' %} 2 | 3 | {% block main %} 4 |
5 |
6 | {% endblock %} 7 | 8 | {% block footer %} 9 | {% endblock %} 10 | 11 | {% block pageData %} 12 | 17 | {% endblock %} 18 | 19 | {% block libcss %} 20 | 21 | {% endblock %} 22 | 23 | {% block libjs %} 24 | {{ super() }} 25 | 26 | {% endblock %} 27 | 28 | {% block devjs %} 29 | {{ loadjs('entry/settings/settings.js') }} 30 | {% endblock %} -------------------------------------------------------------------------------- /views/pages/tag/tag.njk: -------------------------------------------------------------------------------- 1 | {% extends '../../layout/default.njk' %} 2 | 3 | {% block main %} 4 |
5 |
6 | {% endblock %} 7 | 8 | {% block footer %} 9 | {% endblock %} 10 | 11 | {% block pageData %} 12 | 18 | {% endblock %} 19 | 20 | {% block devjs %} 21 | {{ loadjs('entry/tag/tag.js') }} 22 | {% endblock %} -------------------------------------------------------------------------------- /views/pages/tag/tagDetail.njk: -------------------------------------------------------------------------------- 1 | {% extends '../../layout/default.njk' %} 2 | 3 | {% block main %} 4 |
5 |
6 |
7 |
{{tag.name}}
8 |
{{tag.followerCount}} 关注,{{tag.articleCount}} 文章
9 |
10 |
11 |
12 |
13 | {% endblock %} 14 | 15 | {% block pageData %} 16 | 20 | {% endblock %} 21 | 22 | {% block devjs %} 23 | {{ loadjs('entry/tag/tagDetail.js') }} 24 | {% endblock %} -------------------------------------------------------------------------------- /views/pages/user/user.njk: -------------------------------------------------------------------------------- 1 | {% extends '../../layout/default.njk' %} 2 | 3 | {% block main %} 4 |
5 |
6 | {% endblock %} 7 | 8 | {% block footer %} 9 | {% endblock %} 10 | 11 | {% block pageData %} 12 | 21 | {% endblock %} 22 | 23 | {% block devjs %} 24 | {{ loadjs('entry/user/user.js') }} 25 | {% endblock %} --------------------------------------------------------------------------------