├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── Makefile ├── README.md ├── cmd ├── api │ └── server.go ├── cobra.go ├── convert │ └── server.go ├── download │ └── server.go ├── search │ └── server.go └── task │ └── server.go ├── config ├── in-cluster.yaml └── rules │ └── biquge.yaml ├── docs ├── image │ ├── 1.png │ ├── 2.png │ ├── 3.png │ ├── 4.png │ ├── books.png │ ├── category.png │ ├── chapters.png │ ├── logo.png │ ├── upload.png │ └── wx.jpg └── install │ └── book.sql ├── go.mod ├── go.sum ├── main.go ├── pkg ├── api │ ├── admin │ │ ├── controllers │ │ │ ├── base.go │ │ │ ├── books.go │ │ │ ├── category.go │ │ │ ├── chapters.go │ │ │ ├── comicCategory.go │ │ │ ├── comicChapters.go │ │ │ ├── comicComments.go │ │ │ ├── comics.go │ │ │ ├── comments.go │ │ │ ├── config.go │ │ │ ├── distributor .go │ │ │ ├── distributorLevel .go │ │ │ ├── rule.go │ │ │ ├── setting.go │ │ │ ├── task.go │ │ │ ├── upload.go │ │ │ └── users.go │ │ ├── dao │ │ │ ├── book.go │ │ │ ├── books.go │ │ │ ├── category.go │ │ │ ├── chapter.go │ │ │ ├── chapters.go │ │ │ ├── comicCategory.go │ │ │ ├── comicChapters.go │ │ │ ├── comicComment.go │ │ │ ├── comics.go │ │ │ ├── comment.go │ │ │ ├── config.go │ │ │ ├── distributor.go │ │ │ ├── distributorLevel.go │ │ │ ├── spider.go │ │ │ ├── task.go │ │ │ └── users.go │ │ ├── domain │ │ │ └── task │ │ │ │ ├── cron.go │ │ │ │ ├── helper.go │ │ │ │ ├── task.go │ │ │ │ └── task_lock.go │ │ ├── dto │ │ │ ├── book.go │ │ │ ├── books.go │ │ │ ├── category.go │ │ │ ├── chapter.go │ │ │ ├── chapters.go │ │ │ ├── comicCategory.go │ │ │ ├── comicChapters.go │ │ │ ├── comicComment.go │ │ │ ├── comics.go │ │ │ ├── comment.go │ │ │ ├── config.go │ │ │ ├── distributor.go │ │ │ ├── distributorLevel.go │ │ │ ├── general.go │ │ │ ├── init.go │ │ │ ├── task.go │ │ │ ├── upload.go │ │ │ └── users.go │ │ ├── model │ │ │ ├── base.go │ │ │ ├── book.go │ │ │ ├── books.go │ │ │ ├── category.go │ │ │ ├── chapter.go │ │ │ ├── chapters.go │ │ │ ├── comicCategory.go │ │ │ ├── comicChapters.go │ │ │ ├── comicComments.go │ │ │ ├── comics.go │ │ │ ├── comments.go │ │ │ ├── config.go │ │ │ ├── distributor.go │ │ │ ├── distributorLevel.go │ │ │ ├── spider.go │ │ │ ├── task.go │ │ │ └── users.go │ │ └── service │ │ │ ├── book.go │ │ │ ├── books.go │ │ │ ├── category.go │ │ │ ├── chapter.go │ │ │ ├── chapters.go │ │ │ ├── comicCategory.go │ │ │ ├── comicChapters.go │ │ │ ├── comicComments.go │ │ │ ├── comics.go │ │ │ ├── comments.go │ │ │ ├── config.go │ │ │ ├── distributor.go │ │ │ ├── distributorLevel.go │ │ │ ├── task.go │ │ │ ├── upload.go │ │ │ └── users.go │ ├── front │ │ ├── controllers │ │ │ ├── books.go │ │ │ ├── chapters.go │ │ │ ├── users.go │ │ │ └── wechat.go │ │ ├── dao │ │ │ ├── books.go │ │ │ ├── category.go │ │ │ ├── chapters.go │ │ │ ├── config.go │ │ │ └── user.go │ │ ├── domain │ │ │ ├── auth │ │ │ │ ├── account.go │ │ │ │ ├── auth.go │ │ │ │ ├── login │ │ │ │ │ └── general.go │ │ │ │ └── token.go │ │ │ └── wechat │ │ │ │ └── subscribe.go │ │ ├── dto │ │ │ ├── books.go │ │ │ ├── chapters.go │ │ │ ├── general.go │ │ │ ├── init.go │ │ │ └── user.go │ │ └── service │ │ │ ├── books.go │ │ │ ├── chapters.go │ │ │ ├── user.go │ │ │ └── wechat.go │ ├── middleware │ │ ├── auth.go │ │ ├── cors.go │ │ └── file.go │ ├── router │ │ ├── admin.go │ │ ├── front.go │ │ └── router.go │ └── utils │ │ ├── e │ │ └── code.go │ │ ├── file │ │ └── file.go │ │ ├── log │ │ └── logger.go │ │ ├── upload │ │ └── upload.go │ │ └── utils.go ├── common │ ├── cache │ │ ├── init.go │ │ └── redis.go │ ├── db │ │ ├── db.go │ │ ├── db_test.go │ │ ├── mongo.go │ │ └── mongo_test.go │ ├── enu.go │ └── types.go ├── crawler │ ├── core │ │ ├── auto_migrate.go │ │ ├── colly.go │ │ ├── context.go │ │ ├── errors.go │ │ ├── output.go │ │ ├── spider.go │ │ ├── task.go │ │ ├── task_config.go │ │ └── task_rule.go │ └── novels │ │ ├── aoyuge │ │ ├── book.go │ │ └── file.go │ │ ├── base.go │ │ ├── biquge │ │ ├── book.go │ │ ├── chapter.go │ │ └── list.go │ │ ├── dingdian │ │ └── dingdian.go │ │ ├── fanfan │ │ └── book.go │ │ └── store.go ├── down │ ├── convert.go │ ├── download.go │ ├── initstore.go │ ├── job.go │ ├── output │ │ ├── epub.go │ │ ├── md.go │ │ ├── output.go │ │ └── txt.go │ ├── search.go │ ├── site │ │ ├── 81new.go │ │ ├── biquge.go │ │ ├── biquge3.go │ │ ├── dingdian1.go │ │ ├── ffxs.go │ │ ├── jjxs.go │ │ ├── site.go │ │ ├── site_chromedp.go │ │ └── site_ph.go │ ├── store │ │ └── store.go │ ├── syncstore.go │ └── util.go ├── h5 │ ├── .babelrc │ ├── .editorconfig │ ├── .eslintignore │ ├── .eslintrc.js │ ├── .gitignore │ ├── .postcssrc.js │ ├── README.md │ ├── build │ │ ├── build.js │ │ ├── check-versions.js │ │ ├── utils.js │ │ ├── vue-loader.conf.js │ │ ├── webpack.base.conf.js │ │ ├── webpack.dev.conf.js │ │ ├── webpack.prod.conf.js │ │ └── webpack.test.conf.js │ ├── config │ │ ├── dev.env.js │ │ ├── index.js │ │ ├── prod.env.js │ │ └── test.env.js │ ├── index.html │ ├── package-lock.json │ ├── package.json │ ├── src │ │ ├── App.vue │ │ ├── api │ │ │ └── index.js │ │ ├── assets │ │ │ ├── less │ │ │ │ ├── common.less │ │ │ │ ├── icons.less │ │ │ │ └── reset.css │ │ │ ├── lock.svg │ │ │ ├── logo.svg │ │ │ └── user.svg │ │ ├── components │ │ │ ├── BookDetail.vue │ │ │ ├── BookShelf.vue │ │ │ ├── Category.vue │ │ │ ├── Home.vue │ │ │ ├── Login.vue │ │ │ ├── Personal.vue │ │ │ ├── Reader.vue │ │ │ ├── Register.vue │ │ │ ├── Search.vue │ │ │ ├── common │ │ │ │ ├── BottomMenu.vue │ │ │ │ ├── Fonts.vue │ │ │ │ ├── HorizontalList.vue │ │ │ │ ├── Similar.vue │ │ │ │ ├── TitleList.vue │ │ │ │ ├── TopMenu.vue │ │ │ │ └── VerticalList.vue │ │ │ └── rate │ │ │ │ ├── Rate.vue │ │ │ │ ├── star_half.png │ │ │ │ ├── star_off.png │ │ │ │ └── star_on.png │ │ ├── main.js │ │ ├── permission.js │ │ ├── router │ │ │ └── index.js │ │ ├── store │ │ │ ├── index.js │ │ │ └── modules │ │ │ │ ├── actions.js │ │ │ │ ├── mutations.js │ │ │ │ ├── state.js │ │ │ │ └── types.js │ │ └── utils │ │ │ ├── auth.js │ │ │ └── request.js │ └── static │ │ └── images │ │ ├── 1.jpg │ │ ├── 2.jpg │ │ ├── 3.jpg │ │ ├── 4.jpg │ │ ├── 5.jpg │ │ ├── sprite.0.50.png │ │ └── sprite@2x.0.50.png └── ui │ ├── .editorconfig │ ├── .env.development │ ├── .env.production │ ├── .env.staging │ ├── .eslintignore │ ├── .eslintrc.js │ ├── .gitignore │ ├── .travis.yml │ ├── LICENSE │ ├── README.md │ ├── README.zh-CN.md │ ├── babel.config.js │ ├── build │ └── index.js │ ├── jest.config.js │ ├── jsconfig.json │ ├── mock │ ├── index.js │ └── mock-server.js │ ├── package.json │ ├── plop-templates │ ├── component │ │ ├── index.hbs │ │ └── prompt.js │ ├── store │ │ ├── index.hbs │ │ └── prompt.js │ ├── utils.js │ └── view │ │ ├── index.hbs │ │ └── prompt.js │ ├── plopfile.js │ ├── postcss.config.js │ ├── public │ ├── favicon.ico │ └── index.html │ ├── readme.md │ ├── src │ ├── App.vue │ ├── api │ │ ├── auth-admin │ │ │ ├── auth.js │ │ │ ├── enum │ │ │ │ └── index.js │ │ │ ├── request │ │ │ │ └── index.js │ │ │ └── user.js │ │ ├── lime-admin │ │ │ ├── book.js │ │ │ ├── category.js │ │ │ ├── chapter.js │ │ │ ├── comic.js │ │ │ ├── comicCategory.js │ │ │ ├── comicChapter.js │ │ │ ├── comicComment.js │ │ │ ├── comment.js │ │ │ ├── distributor.js │ │ │ ├── distributorLevel.js │ │ │ ├── enum │ │ │ │ └── index.js │ │ │ ├── request │ │ │ │ └── index.js │ │ │ ├── setting.js │ │ │ ├── upload.js │ │ │ └── users.js │ │ └── user.js │ ├── assets │ │ ├── 401_images │ │ │ └── 401.gif │ │ ├── 404_images │ │ │ ├── 404.png │ │ │ └── 404_cloud.png │ │ ├── custom-theme │ │ │ ├── fonts │ │ │ │ ├── element-icons.ttf │ │ │ │ └── element-icons.woff │ │ │ └── index.css │ │ └── logo-min.png │ ├── components │ │ ├── BackToTop │ │ │ └── index.vue │ │ ├── Breadcrumb │ │ │ └── index.vue │ │ ├── Charts │ │ │ ├── Keyboard.vue │ │ │ ├── LineMarker.vue │ │ │ ├── MixChart.vue │ │ │ ├── keyboard.vue │ │ │ ├── lineMarker.vue │ │ │ ├── mixChart.vue │ │ │ └── mixins │ │ │ │ └── resize.js │ │ ├── DndList │ │ │ └── index.vue │ │ ├── DragSelect │ │ │ └── index.vue │ │ ├── Dropzone │ │ │ └── index.vue │ │ ├── ErrorLog │ │ │ └── index.vue │ │ ├── GithubCorner │ │ │ └── index.vue │ │ ├── Hamburger │ │ │ └── index.vue │ │ ├── HeaderSearch │ │ │ └── index.vue │ │ ├── ImageCropper │ │ │ ├── index.vue │ │ │ └── utils │ │ │ │ ├── data2blob.js │ │ │ │ ├── effectRipple.js │ │ │ │ ├── language.js │ │ │ │ └── mimes.js │ │ ├── JsonEditor │ │ │ └── index.vue │ │ ├── Kanban │ │ │ └── index.vue │ │ ├── MDinput │ │ │ └── index.vue │ │ ├── MarkdownEditor │ │ │ ├── default-options.js │ │ │ └── index.vue │ │ ├── Pagination │ │ │ └── index.vue │ │ ├── PanThumb │ │ │ └── index.vue │ │ ├── RightPanel │ │ │ └── index.vue │ │ ├── Screenfull │ │ │ └── index.vue │ │ ├── Share │ │ │ ├── DropdownMenu.vue │ │ │ └── dropdownMenu.vue │ │ ├── SizeSelect │ │ │ └── index.vue │ │ ├── Sticky │ │ │ └── index.vue │ │ ├── SvgIcon │ │ │ └── index.vue │ │ ├── TextHoverEffect │ │ │ └── Mallki.vue │ │ ├── ThemePicker │ │ │ └── index.vue │ │ ├── Tinymce │ │ │ ├── components │ │ │ │ └── EditorImage.vue │ │ │ ├── dynamicLoadScript.js │ │ │ ├── index.vue │ │ │ ├── plugins.js │ │ │ └── toolbar.js │ │ ├── Upload │ │ │ ├── SingleImage.vue │ │ │ ├── SingleImage2.vue │ │ │ ├── SingleImage3.vue │ │ │ ├── singleImage.vue │ │ │ ├── singleImage2.vue │ │ │ └── singleImage3.vue │ │ └── UploadExcel │ │ │ └── index.vue │ ├── directive │ │ ├── clipboard │ │ │ ├── clipboard.js │ │ │ └── index.js │ │ ├── el-drag-dialog │ │ │ ├── drag.js │ │ │ └── index.js │ │ ├── el-table │ │ │ ├── adaptive.js │ │ │ └── index.js │ │ ├── permission │ │ │ ├── index.js │ │ │ └── permission.js │ │ ├── sticky.js │ │ └── waves │ │ │ ├── index.js │ │ │ ├── waves.css │ │ │ └── waves.js │ ├── filters │ │ └── index.js │ ├── icons │ │ ├── index.js │ │ ├── svg │ │ │ ├── 404.svg │ │ │ ├── bug.svg │ │ │ ├── chart.svg │ │ │ ├── clipboard.svg │ │ │ ├── component.svg │ │ │ ├── dashboard.svg │ │ │ ├── documentation.svg │ │ │ ├── drag.svg │ │ │ ├── edit.svg │ │ │ ├── education.svg │ │ │ ├── email.svg │ │ │ ├── example.svg │ │ │ ├── excel.svg │ │ │ ├── exit-fullscreen.svg │ │ │ ├── eye-open.svg │ │ │ ├── eye.svg │ │ │ ├── form.svg │ │ │ ├── fullscreen.svg │ │ │ ├── guide.svg │ │ │ ├── icon.svg │ │ │ ├── international.svg │ │ │ ├── language.svg │ │ │ ├── link.svg │ │ │ ├── list.svg │ │ │ ├── lock.svg │ │ │ ├── message.svg │ │ │ ├── money.svg │ │ │ ├── nested.svg │ │ │ ├── password.svg │ │ │ ├── pdf.svg │ │ │ ├── people.svg │ │ │ ├── peoples.svg │ │ │ ├── qq.svg │ │ │ ├── search.svg │ │ │ ├── shopping.svg │ │ │ ├── size.svg │ │ │ ├── skill.svg │ │ │ ├── star.svg │ │ │ ├── tab.svg │ │ │ ├── table.svg │ │ │ ├── theme.svg │ │ │ ├── tree-table.svg │ │ │ ├── tree.svg │ │ │ ├── user.svg │ │ │ ├── wechat.svg │ │ │ └── zip.svg │ │ └── svgo.yml │ ├── layout │ │ ├── components │ │ │ ├── AppMain.vue │ │ │ ├── Navbar.vue │ │ │ ├── Settings │ │ │ │ └── index.vue │ │ │ ├── Sidebar │ │ │ │ ├── FixiOSBug.js │ │ │ │ ├── Item.vue │ │ │ │ ├── Link.vue │ │ │ │ ├── Logo.vue │ │ │ │ ├── SidebarItem.vue │ │ │ │ └── index.vue │ │ │ ├── TagsView │ │ │ │ ├── ScrollPane.vue │ │ │ │ └── index.vue │ │ │ └── index.js │ │ ├── index.vue │ │ └── mixin │ │ │ └── ResizeHandler.js │ ├── main.js │ ├── permission.js │ ├── router │ │ └── index.js │ ├── settings.js │ ├── store │ │ ├── getters.js │ │ ├── index.js │ │ └── modules │ │ │ ├── app.js │ │ │ ├── errorLog.js │ │ │ ├── permission.js │ │ │ ├── settings.js │ │ │ ├── tagsView.js │ │ │ └── user.js │ ├── styles │ │ ├── btn.scss │ │ ├── element-ui.scss │ │ ├── element-variables.scss │ │ ├── index.scss │ │ ├── mixin.scss │ │ ├── sidebar.scss │ │ ├── transition.scss │ │ └── variables.scss │ ├── utils │ │ ├── auth.js │ │ ├── clipboard.js │ │ ├── error-log.js │ │ ├── get-page-title.js │ │ ├── index.js │ │ ├── open-window.js │ │ ├── permission.js │ │ ├── request.js │ │ ├── scroll-to.js │ │ └── validate.js │ ├── vendor │ │ ├── Export2Excel.js │ │ └── Export2Zip.js │ └── views │ │ ├── books │ │ ├── components │ │ │ └── selectedpanel.vue │ │ ├── create.vue │ │ ├── emun │ │ │ └── index.js │ │ ├── list.vue │ │ ├── update.vue │ │ ├── upload.vue │ │ └── view.vue │ │ ├── category │ │ ├── emun │ │ │ └── index.js │ │ └── list.vue │ │ ├── chapters │ │ ├── create.vue │ │ ├── list.vue │ │ └── update.vue │ │ ├── comicCategory │ │ ├── emun │ │ │ └── index.js │ │ └── list.vue │ │ ├── comicChapters │ │ ├── create.vue │ │ ├── list.vue │ │ └── update.vue │ │ ├── comicComments │ │ └── list.vue │ │ ├── comics │ │ ├── components │ │ │ └── selectedpanel.vue │ │ ├── create.vue │ │ ├── emun │ │ │ └── index.js │ │ ├── list.vue │ │ ├── update.vue │ │ └── view.vue │ │ ├── comments │ │ └── list.vue │ │ ├── dashboard │ │ └── index.vue │ │ ├── distributor │ │ ├── levelCreate.vue │ │ ├── levelUpdate.vue │ │ ├── levellist.vue │ │ ├── list.vue │ │ ├── setting.vue │ │ └── settlementSetting.vue │ │ ├── error-page │ │ ├── 401.vue │ │ └── 404.vue │ │ ├── guide │ │ ├── index.vue │ │ └── steps.js │ │ ├── login │ │ ├── auth-redirect.vue │ │ ├── components │ │ │ └── SocialSignin.vue │ │ └── index.vue │ │ ├── permission │ │ ├── components │ │ │ └── SwitchRoles.vue │ │ ├── directive.vue │ │ ├── page.vue │ │ └── role.vue │ │ ├── redirect │ │ └── index.vue │ │ ├── setting │ │ ├── wechat.vue │ │ └── wechatMenu.vue │ │ └── users │ │ ├── components │ │ └── selectedpanel.vue │ │ ├── create.vue │ │ ├── emun │ │ └── index.js │ │ ├── list.vue │ │ └── update.vue │ ├── tests │ └── unit │ │ ├── .eslintrc.js │ │ ├── components │ │ ├── Hamburger.spec.js │ │ └── SvgIcon.spec.js │ │ └── utils │ │ ├── formatTime.spec.js │ │ ├── parseTime.spec.js │ │ └── validate.spec.js │ └── vue.config.js └── scripts ├── apppkg.sh ├── build.sh ├── lime.sql ├── run.sh ├── tag.sh └── version.sh /.gitignore: -------------------------------------------------------------------------------- 1 | lime 2 | .idea/ 3 | data/ 4 | go.sum 5 | config/in-local.yaml 6 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | v0.0.1 init 初始化 4 | V0.1 小说内容管理: 分类管理、小说列表、上传小说、热词列表、评论列表 5 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | SHELL := /bin/bash 2 | BASE_PATH := $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST)))) 3 | 4 | TITLE := $(shell basename $(BASE_PATH)) 5 | SHORT_REV := $(shell git rev-parse HEAD | cut -c1-8) 6 | BUILD_TIME := $(shell date +%Y-%m-%d--%T) 7 | APP_PKG := $(shell $(BASE_PATH)/scripts/apppkg.sh) 8 | UI := $(BASE_PATH)/pkg/ui 9 | TAG := $(shell $(BASE_PATH)/scripts/tag.sh) 10 | export BIN_OUT := $(BASE_PATH)/bin 11 | 12 | all: print 13 | 14 | print: 15 | @echo ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>making print<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<" 16 | @echo SHELL:$(SHELL) 17 | @echo BASE_PATH:$(BASE_PATH) 18 | @echo TITLE:$(TITLE) 19 | @echo SHORT_REV:$(SHORT_REV) 20 | @echo APP_PKG:$(APP_PKG) 21 | @echo BIN_OUT:$(BIN_OUT) 22 | @echo USER:$(USER) 23 | @echo HUB:$(HUB) 24 | @echo UI:$(UI) 25 | @echo TAG:$(TAG) 26 | @echo -e "\n" 27 | build: 28 | @echo ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>make build<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<" 29 | $(shell $(BASE_PATH)/scripts/build.sh) 30 | @echo -e "\n" 31 | run: 32 | @echo ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>run lime<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<" 33 | $(shell $(BASE_PATH)/scripts/run.sh) 34 | @echo -e "\n" 35 | -------------------------------------------------------------------------------- /cmd/cobra.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "github.com/spf13/cobra" 5 | "lime/cmd/api" 6 | "lime/cmd/convert" 7 | "lime/cmd/download" 8 | "lime/cmd/search" 9 | "lime/cmd/task" 10 | "os" 11 | ) 12 | 13 | var rootCmd = &cobra.Command{ 14 | Use: "download", 15 | Short: "download API server", 16 | SilenceUsage: true, 17 | DisableAutoGenTag: true, 18 | Long: `Start download API server`, 19 | PersistentPreRunE: func(*cobra.Command, []string) error { return nil }, 20 | } 21 | 22 | func init() { 23 | rootCmd.AddCommand(api.StartCmd) 24 | rootCmd.AddCommand(download.StartCmd) 25 | rootCmd.AddCommand(convert.StartCmd) 26 | rootCmd.AddCommand(search.StartCmd) 27 | rootCmd.AddCommand(task.StartCmd) 28 | } 29 | 30 | //Execute : run commands 31 | func Execute() { 32 | if err := rootCmd.Execute(); err != nil { 33 | os.Exit(-1) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /config/in-cluster.yaml: -------------------------------------------------------------------------------- 1 | mode: debug 2 | mysql: 3 | dsn: >- 4 | ${lime_MYSQL_USERNAME}:${lime_MYSQL_PASSWORD}@tcp(${lime_MYSQL_HOST}:${lime_MYSQL_PORT})/${lime_MYSQL_DB}?charset=utf8&parseTime=True&loc=Local 5 | pool: 6 | min: 5 7 | max: 20 8 | redis: 9 | host: ${lime_REDIS_HOST}:${lime_REDIS_PORT} 10 | auth: ${lime_REDIS_PASSWORD} 11 | pool: 12 | min: 3 13 | max: 20 14 | mongo: 15 | host: ${lime_MONGO_HOST} 16 | port: ${lime_MONGO_PORT} 17 | db: ${lime_MONGO_DB} 18 | username: ${lime_MONGO_USERNAME} 19 | password: ${lime_MONGO_PASSWORD} 20 | authSource: ${lime_MONGO_AUTHSOURCE} 21 | cors: 22 | allow_credentials: false 23 | allow_headers: 24 | - '*' 25 | allow_methods: 26 | - GET 27 | - POST 28 | - OPTIONS 29 | - PUT 30 | - DELETE 31 | allow_origins: 32 | - '*' 33 | enable: false 34 | max_age: 7200 35 | upload: 36 | image_prefix_url: "http://127.0.0.1:8000" 37 | image_save_path: "data/images/" 38 | image_max_size: 5 39 | image_allow_exts: 40 | - .jpg 41 | - .jpeg 42 | - .png" 43 | auth: 44 | access_secret: "" 45 | refresh_secret: "" 46 | qiniu: 47 | ak: "" 48 | sk: "" 49 | bucket: "" -------------------------------------------------------------------------------- /docs/image/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/limeteam/lime/7708031446a878b090487588b8f4bcca63514282/docs/image/1.png -------------------------------------------------------------------------------- /docs/image/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/limeteam/lime/7708031446a878b090487588b8f4bcca63514282/docs/image/2.png -------------------------------------------------------------------------------- /docs/image/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/limeteam/lime/7708031446a878b090487588b8f4bcca63514282/docs/image/3.png -------------------------------------------------------------------------------- /docs/image/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/limeteam/lime/7708031446a878b090487588b8f4bcca63514282/docs/image/4.png -------------------------------------------------------------------------------- /docs/image/books.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/limeteam/lime/7708031446a878b090487588b8f4bcca63514282/docs/image/books.png -------------------------------------------------------------------------------- /docs/image/category.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/limeteam/lime/7708031446a878b090487588b8f4bcca63514282/docs/image/category.png -------------------------------------------------------------------------------- /docs/image/chapters.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/limeteam/lime/7708031446a878b090487588b8f4bcca63514282/docs/image/chapters.png -------------------------------------------------------------------------------- /docs/image/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/limeteam/lime/7708031446a878b090487588b8f4bcca63514282/docs/image/logo.png -------------------------------------------------------------------------------- /docs/image/upload.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/limeteam/lime/7708031446a878b090487588b8f4bcca63514282/docs/image/upload.png -------------------------------------------------------------------------------- /docs/image/wx.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/limeteam/lime/7708031446a878b090487588b8f4bcca63514282/docs/image/wx.jpg -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "lime/cmd" 5 | ) 6 | 7 | func main() { 8 | cmd.Execute() 9 | } 10 | -------------------------------------------------------------------------------- /pkg/api/admin/controllers/base.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "lime/pkg/api/admin/dto" 6 | "lime/pkg/api/utils/e" 7 | "net/http" 8 | ) 9 | 10 | type BaseController struct { 11 | } 12 | 13 | func (bc *BaseController) BindAndValidate(c *gin.Context, obj interface{}) bool { 14 | if err := dto.Bind(c, obj); err != nil { 15 | failValidate(c, err.Error()) 16 | return false 17 | } 18 | return true 19 | } 20 | 21 | func (bc *BaseController) Resp(c *gin.Context, data map[string]interface{}) { 22 | c.JSON(http.StatusOK, gin.H{ 23 | "code": 200, 24 | "data": data, 25 | }) 26 | } 27 | 28 | func (bc *BaseController) Ok(c *gin.Context, msg string) { 29 | c.JSON(http.StatusOK, gin.H{ 30 | "code": 200, 31 | "msg": msg, 32 | }) 33 | } 34 | 35 | func (bc *BaseController) Fail(c *gin.Context, errs *e.ControllerError) { 36 | c.JSON(http.StatusOK, gin.H{ 37 | "code": errs.Code, 38 | "msg": errs.Message, 39 | "moreinfo": errs.Moreinfo, 40 | }) 41 | } 42 | 43 | func failValidate(c *gin.Context, msg string) { 44 | errs := e.ErrValidation 45 | c.AbortWithStatusJSON(http.StatusOK, gin.H{ 46 | "code": errs.Code, 47 | "msg": errs.Message, 48 | "detail": msg, 49 | }) 50 | } 51 | -------------------------------------------------------------------------------- /pkg/api/admin/controllers/comicComments.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "lime/pkg/api/admin/dto" 6 | "lime/pkg/api/admin/service" 7 | "lime/pkg/api/utils/e" 8 | ) 9 | 10 | var ComicCommentsService = service.ComicCommentervice{} 11 | 12 | type ComicCommentsController struct { 13 | BaseController 14 | } 15 | 16 | func (C *ComicCommentsController) List(c *gin.Context) { 17 | var Dto dto.ComicCommentListDto 18 | if C.BindAndValidate(c, &Dto) { 19 | data, total := ComicCommentsService.List(Dto) 20 | C.Resp(c, map[string]interface{}{ 21 | "list": data, 22 | "total": total, 23 | "page": Dto.Page, 24 | "page_size": Dto.Limit, 25 | }) 26 | } 27 | } 28 | 29 | func (C *ComicCommentsController) Get(c *gin.Context) { 30 | var Dto dto.GeneralGetDto 31 | if C.BindAndValidate(c, &Dto) { 32 | data := ComicCommentsService.InfoOfId(Dto) 33 | if data.Id < 1 { 34 | C.Fail(c, e.ErrIdData) 35 | return 36 | } 37 | C.Resp(c, map[string]interface{}{ 38 | "result": data, 39 | }) 40 | } 41 | } 42 | 43 | func (C *ComicCommentsController) Delete(c *gin.Context) { 44 | var Dto dto.GeneralDelDto 45 | if C.BindAndValidate(c, &Dto) { 46 | affected := ComicCommentsService.Delete(Dto) 47 | if affected <= 0 { 48 | C.Fail(c, e.ErrDelFail) 49 | return 50 | } 51 | C.Ok(c, "删除成功") 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /pkg/api/admin/controllers/comments.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "lime/pkg/api/admin/dto" 6 | "lime/pkg/api/admin/service" 7 | "lime/pkg/api/utils/e" 8 | ) 9 | 10 | var CommentsService = service.CommentService{} 11 | 12 | type CommentsController struct { 13 | BaseController 14 | } 15 | 16 | func (C *CommentsController) List(c *gin.Context) { 17 | var Dto dto.CommentListDto 18 | if C.BindAndValidate(c, &Dto) { 19 | data, total := CommentsService.List(Dto) 20 | C.Resp(c, map[string]interface{}{ 21 | "list": data, 22 | "total": total, 23 | "page": Dto.Page, 24 | "page_size": Dto.Limit, 25 | }) 26 | } 27 | } 28 | 29 | func (C *CommentsController) Get(c *gin.Context) { 30 | var Dto dto.GeneralGetDto 31 | if C.BindAndValidate(c, &Dto) { 32 | data := CommentsService.InfoOfId(Dto) 33 | if data.Id < 1 { 34 | C.Fail(c, e.ErrIdData) 35 | return 36 | } 37 | C.Resp(c, map[string]interface{}{ 38 | "result": data, 39 | }) 40 | } 41 | } 42 | 43 | func (C *CommentsController) Delete(c *gin.Context) { 44 | var Dto dto.GeneralDelDto 45 | if C.BindAndValidate(c, &Dto) { 46 | affected := CommentsService.Delete(Dto) 47 | if affected <= 0 { 48 | C.Fail(c, e.ErrDelFail) 49 | return 50 | } 51 | C.Ok(c, "删除成功") 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /pkg/api/admin/controllers/rule.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | log "github.com/sirupsen/logrus" 6 | "lime/pkg/crawler/core" 7 | ) 8 | 9 | type RuleController struct { 10 | BaseController 11 | } 12 | 13 | func (C *RuleController) GetRuleList(c *gin.Context) { 14 | keys := core.GetTaskRuleKeys() 15 | if len(keys) == 0 { 16 | log.Warnf("task rule is empty") 17 | } 18 | C.Resp(c, map[string]interface{}{ 19 | "list": keys, 20 | "total": len(keys), 21 | }) 22 | } 23 | -------------------------------------------------------------------------------- /pkg/api/admin/controllers/setting.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "github.com/spf13/viper" 6 | "lime/pkg/api/utils" 7 | "strings" 8 | ) 9 | 10 | type SettingController struct { 11 | BaseController 12 | } 13 | 14 | func (C *SettingController) GetTokenAndEncodingAESKey(c *gin.Context) { 15 | C.Resp(c, map[string]interface{}{ 16 | "receiveUrl": viper.GetString("wechat.receiveUrl") + "/v1/wechat/callback", 17 | "token": strings.ToLower(utils.CreateRandomString(32)), 18 | "EncodingAESKey": utils.CreateRandomString(43), 19 | }) 20 | } -------------------------------------------------------------------------------- /pkg/api/admin/controllers/upload.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "github.com/spf13/viper" 6 | "lime/pkg/api/admin/service" 7 | ) 8 | 9 | var UploadService = service.UploadService{} 10 | 11 | type UploadController struct { 12 | BaseController 13 | } 14 | 15 | func (C *UploadController) QiniuToken(c *gin.Context) { 16 | token := UploadService.GetToken() 17 | C.Resp(c, map[string]interface{}{ 18 | "token": token, 19 | "domain": viper.GetString("qiniu.domain"), 20 | }) 21 | } 22 | -------------------------------------------------------------------------------- /pkg/api/admin/dao/book.go: -------------------------------------------------------------------------------- 1 | package dao 2 | 3 | import ( 4 | "github.com/jinzhu/gorm" 5 | "lime/pkg/api/admin/dto" 6 | "lime/pkg/api/admin/model" 7 | "lime/pkg/common/db" 8 | ) 9 | 10 | type BookDao struct { 11 | } 12 | 13 | func (u BookDao) Get(id int) model.Book { 14 | var Book model.Book 15 | db := db.GetGormDB() 16 | db.Where("id = ?", id).First(&Book) 17 | return Book 18 | } 19 | 20 | func (u BookDao) List(listDto dto.BookListDto) ([]model.Book, int64) { 21 | var Book []model.Book 22 | var total int64 23 | db := db.GetGormDB() 24 | db.Preload("Book").Offset(listDto.Skip).Limit(listDto.Limit).Find(&Book) 25 | db.Model(&model.Book{}).Count(&total) 26 | return Book, total 27 | } 28 | 29 | func (u BookDao) Create(Book *model.Book) *gorm.DB { 30 | db := db.GetGormDB() 31 | return db.Save(Book) 32 | } 33 | 34 | func (u BookDao) Update(Book *model.Book) *gorm.DB { 35 | db := db.GetGormDB() 36 | return db.Save(Book) 37 | } 38 | 39 | func (u BookDao) Delete(Book *model.Book) *gorm.DB { 40 | db := db.GetGormDB() 41 | return db.Delete(Book) 42 | } 43 | -------------------------------------------------------------------------------- /pkg/api/admin/dao/category.go: -------------------------------------------------------------------------------- 1 | package dao 2 | 3 | import ( 4 | "github.com/jinzhu/gorm" 5 | "lime/pkg/api/admin/dto" 6 | "lime/pkg/api/admin/model" 7 | "lime/pkg/common/db" 8 | ) 9 | 10 | type CategoryDao struct { 11 | } 12 | 13 | func (c CategoryDao) Get(id int) model.Category { 14 | var Category model.Category 15 | db := db.GetGormDB() 16 | db.Where("id = ?", id).First(&Category) 17 | return Category 18 | } 19 | 20 | func (c CategoryDao) GetAll() []model.Category { 21 | var Category []model.Category 22 | db := db.GetGormDB() 23 | db.Model(&model.Category{}).Find(&Category) 24 | return Category 25 | } 26 | 27 | func (c CategoryDao) List(listDto dto.CategoryListDto) ([]model.Category, int64) { 28 | var Category []model.Category 29 | var total int64 30 | db := db.GetGormDB() 31 | db.Offset(listDto.Skip).Limit(listDto.Limit).Find(&Category) 32 | db.Model(&model.Category{}).Count(&total) 33 | return Category, total 34 | } 35 | 36 | func (c CategoryDao) Create(Category *model.Category) *gorm.DB { 37 | db := db.GetGormDB() 38 | return db.Save(Category) 39 | } 40 | 41 | // Update 42 | func (c CategoryDao) Update(Category *model.Category, ups map[string]interface{}) *gorm.DB { 43 | db := db.GetGormDB() 44 | return db.Model(Category).Update(ups) 45 | } 46 | 47 | func (c CategoryDao) Delete(Category *model.Category) *gorm.DB { 48 | db := db.GetGormDB() 49 | return db.Delete(Category) 50 | } 51 | -------------------------------------------------------------------------------- /pkg/api/admin/dao/chapter.go: -------------------------------------------------------------------------------- 1 | package dao 2 | 3 | import ( 4 | "github.com/jinzhu/gorm" 5 | "lime/pkg/api/admin/dto" 6 | "lime/pkg/api/admin/model" 7 | "lime/pkg/common/db" 8 | ) 9 | 10 | type ChapterDao struct { 11 | } 12 | 13 | func (u ChapterDao) Get(id int) model.Chapter { 14 | var Chapter model.Chapter 15 | db := db.GetGormDB() 16 | db.Where("id = ?", id).First(&Chapter) 17 | return Chapter 18 | } 19 | 20 | func (u ChapterDao) List(listDto dto.GeneralListDto) ([]model.Chapter, int64) { 21 | var Chapter []model.Chapter 22 | var total int64 23 | db := db.GetGormDB() 24 | db.Preload("Chapter").Offset(listDto.Skip).Limit(listDto.Limit).Find(&Chapter) 25 | db.Model(&model.Chapter{}).Count(&total) 26 | return Chapter, total 27 | } 28 | 29 | func (u ChapterDao) Create(Chapter *model.Chapter) *gorm.DB { 30 | db := db.GetGormDB() 31 | return db.Save(Chapter) 32 | } 33 | 34 | func (u ChapterDao) Update(Chapter *model.Chapter) *gorm.DB { 35 | db := db.GetGormDB() 36 | return db.Save(Chapter) 37 | } 38 | 39 | func (u ChapterDao) Delete(Chapter *model.Chapter) *gorm.DB { 40 | db := db.GetGormDB() 41 | return db.Delete(Chapter) 42 | } 43 | -------------------------------------------------------------------------------- /pkg/api/admin/dao/comicCategory.go: -------------------------------------------------------------------------------- 1 | package dao 2 | 3 | import ( 4 | "github.com/jinzhu/gorm" 5 | "lime/pkg/api/admin/dto" 6 | "lime/pkg/api/admin/model" 7 | "lime/pkg/common/db" 8 | ) 9 | 10 | type ComicCategoryDao struct { 11 | } 12 | 13 | func (c ComicCategoryDao) Get(id int) model.ComicCategory { 14 | var Category model.ComicCategory 15 | db := db.GetGormDB() 16 | db.Where("id = ?", id).First(&Category) 17 | return Category 18 | } 19 | 20 | func (c ComicCategoryDao) GetAll() []model.ComicCategory { 21 | var Category []model.ComicCategory 22 | db := db.GetGormDB() 23 | db.Model(&model.ComicCategory{}).Find(&Category) 24 | return Category 25 | } 26 | 27 | func (c ComicCategoryDao) List(listDto dto.ComicCategoryListDto) ([]model.ComicCategory, int64) { 28 | var Category []model.ComicCategory 29 | var total int64 30 | db := db.GetGormDB() 31 | db.Offset(listDto.Skip).Limit(listDto.Limit).Find(&Category) 32 | db.Model(&model.ComicCategory{}).Count(&total) 33 | return Category, total 34 | } 35 | 36 | func (c ComicCategoryDao) Create(Category *model.ComicCategory) *gorm.DB { 37 | db := db.GetGormDB() 38 | return db.Save(Category) 39 | } 40 | 41 | // Update 42 | func (c ComicCategoryDao) Update(Category *model.ComicCategory, ups map[string]interface{}) *gorm.DB { 43 | db := db.GetGormDB() 44 | return db.Model(Category).Update(ups) 45 | } 46 | 47 | func (c ComicCategoryDao) Delete(Category *model.ComicCategory) *gorm.DB { 48 | db := db.GetGormDB() 49 | return db.Delete(Category) 50 | } 51 | -------------------------------------------------------------------------------- /pkg/api/admin/dao/comicComment.go: -------------------------------------------------------------------------------- 1 | package dao 2 | 3 | import ( 4 | "github.com/jinzhu/gorm" 5 | "lime/pkg/api/admin/dto" 6 | "lime/pkg/api/admin/model" 7 | "lime/pkg/common/db" 8 | ) 9 | 10 | type ComicCommentDao struct { 11 | } 12 | 13 | func (c ComicCommentDao) Get(id int) model.ComicComments { 14 | var Comment model.ComicComments 15 | db := db.GetGormDB() 16 | db.Where("id = ?", id).First(&Comment) 17 | return Comment 18 | } 19 | 20 | func (c ComicCommentDao) GetAll() []model.ComicComments { 21 | var Comment []model.ComicComments 22 | db := db.GetGormDB() 23 | db.Model(&model.ComicComments{}).Find(&Comment) 24 | return Comment 25 | } 26 | 27 | func (c ComicCommentDao) List(listDto dto.ComicCommentListDto) ([]model.ComicComments, int64) { 28 | var Comment []model.ComicComments 29 | var total int64 30 | db := db.GetGormDB() 31 | db.Offset(listDto.Skip).Limit(listDto.Limit).Find(&Comment) 32 | db.Model(&model.ComicComments{}).Count(&total) 33 | return Comment, total 34 | } 35 | 36 | func (c ComicCommentDao) Create(Comment *model.ComicComments) *gorm.DB { 37 | db := db.GetGormDB() 38 | return db.Save(Comment) 39 | } 40 | 41 | func (c ComicCommentDao) Delete(Comment *model.ComicComments) *gorm.DB { 42 | db := db.GetGormDB() 43 | return db.Delete(Comment) 44 | } 45 | -------------------------------------------------------------------------------- /pkg/api/admin/dao/comment.go: -------------------------------------------------------------------------------- 1 | package dao 2 | 3 | import ( 4 | "github.com/jinzhu/gorm" 5 | "lime/pkg/api/admin/dto" 6 | "lime/pkg/api/admin/model" 7 | "lime/pkg/common/db" 8 | ) 9 | 10 | type CommentDao struct { 11 | } 12 | 13 | func (c CommentDao) Get(id int) model.Comments { 14 | var Comment model.Comments 15 | db := db.GetGormDB() 16 | db.Where("id = ?", id).First(&Comment) 17 | return Comment 18 | } 19 | 20 | func (c CommentDao) GetAll() []model.Comments { 21 | var Comment []model.Comments 22 | db := db.GetGormDB() 23 | db.Model(&model.Comments{}).Find(&Comment) 24 | return Comment 25 | } 26 | 27 | func (c CommentDao) List(listDto dto.CommentListDto) ([]model.Comments, int64) { 28 | var Comment []model.Comments 29 | var total int64 30 | db := db.GetGormDB() 31 | db.Offset(listDto.Skip).Limit(listDto.Limit).Find(&Comment) 32 | db.Model(&model.Comments{}).Count(&total) 33 | return Comment, total 34 | } 35 | 36 | func (c CommentDao) Create(Comment *model.Comments) *gorm.DB { 37 | db := db.GetGormDB() 38 | return db.Save(Comment) 39 | } 40 | 41 | func (c CommentDao) Delete(Comment *model.Comments) *gorm.DB { 42 | db := db.GetGormDB() 43 | return db.Delete(Comment) 44 | } 45 | -------------------------------------------------------------------------------- /pkg/api/admin/dao/spider.go: -------------------------------------------------------------------------------- 1 | package dao 2 | 3 | import ( 4 | "fmt" 5 | "github.com/globalsign/mgo/bson" 6 | "lime/pkg/api/admin/model" 7 | "lime/pkg/common/db" 8 | "time" 9 | ) 10 | 11 | type SpiderDao struct{} 12 | 13 | func (sd SpiderDao) Add(row map[int]interface{}) error { 14 | var spider model.Spider 15 | s, c := db.GetCol("spiders") 16 | defer s.Close() 17 | spider.Id = bson.NewObjectId() 18 | spider.AppName = fmt.Sprintf("%v", row[0]) 19 | 20 | spider.CreatedAt = time.Now() 21 | spider.UpdatedAt = time.Now() 22 | if err := c.Insert(&spider); err != nil { 23 | return err 24 | } 25 | return nil 26 | } 27 | 28 | func (sd SpiderDao) InitYaml(row map[int]interface{}) error { 29 | 30 | return nil 31 | } 32 | 33 | func (sd SpiderDao) AddYaml(row map[int]interface{}) error { 34 | 35 | return nil 36 | } 37 | -------------------------------------------------------------------------------- /pkg/api/admin/domain/task/task_lock.go: -------------------------------------------------------------------------------- 1 | package task 2 | 3 | import ( 4 | "sync" 5 | ) 6 | 7 | var taskLock *TaskLock 8 | 9 | type TaskLock struct { 10 | taskLock map[int]bool 11 | sync.Mutex 12 | } 13 | 14 | func init() { 15 | taskLock = &TaskLock{ 16 | taskLock: make(map[int]bool), 17 | } 18 | } 19 | 20 | func (tl *TaskLock) IsRunning(taskid int) bool { 21 | tl.Lock() 22 | defer tl.Unlock() 23 | if tl.taskLock[taskid] { 24 | return true 25 | } 26 | tl.taskLock[taskid] = true 27 | return false 28 | } 29 | 30 | func (tl *TaskLock) Complete(taskid int) { 31 | tl.Lock() 32 | defer tl.Unlock() 33 | delete(tl.taskLock, taskid) 34 | } 35 | -------------------------------------------------------------------------------- /pkg/api/admin/dto/chapter.go: -------------------------------------------------------------------------------- 1 | package dto 2 | 3 | import "time" 4 | 5 | type ChapterCreateDto struct { 6 | Id int `json:"id"` 7 | Book_id int `form:"book_id" json:"book_id"` 8 | Title string `form:"title" json:"title"` 9 | Content string `form:"content" json:"content"` 10 | Status int `form:"status" json:"status"` 11 | Url string `form:"url" json:"url"` 12 | CreateTime time.Time `type(datetime)" json:"create_time"` 13 | LastLoginTime time.Time `type(datetime)" json:"-"` 14 | } 15 | 16 | type ChapterEditDto struct { 17 | Id int `json:"id"` 18 | Book_id int `form:"book_id" json:"book_id"` 19 | Title string `form:"title" json:"title"` 20 | Content string `form:"content" json:"content"` 21 | Status int `form:"status" json:"status"` 22 | Url string `form:"url" json:"url"` 23 | } 24 | -------------------------------------------------------------------------------- /pkg/api/admin/dto/comicCategory.go: -------------------------------------------------------------------------------- 1 | package dto 2 | 3 | import "time" 4 | 5 | type ComicCategoryListDto struct { 6 | Name string `form:"name" json:"name" ` 7 | Comic_num int `form:"comic_num,default=0" json:"comic_num" ` 8 | Sort int `form:"sort" json:"sort" ` 9 | CreatedAt time.Time `form:"created_at" json:"created_at"` 10 | UpdatedAt time.Time `form:"updated_at" json:"updated_at"` 11 | DeletedAt *time.Time `form:"deleted_at" json:"deleted_at"` 12 | Page string `form:"page" json:"page" ` 13 | Skip int `form:"skip,default=0" json:"skip"` 14 | Limit int `form:"limit,default=20" json:"limit" binding:"max=100"` 15 | } 16 | 17 | type ComicCategoryCreateDto struct { 18 | Name string `form:"name" json:"name" ` 19 | Comic_num int `form:"comic_num,default=0" json:"comic_num" ` 20 | Sort int `form:"sort" json:"sort" ` 21 | CreatedAt time.Time `form:"created_at" json:"created_at"` 22 | UpdatedAt time.Time `form:"updated_at" json:"updated_at"` 23 | } 24 | 25 | type ComicCategoryEditDto struct { 26 | Id int `uri:"id" json:"id" binding:"required"` 27 | Name string `form:"name" json:"name" binding:"required"` 28 | Comic_num int `form:"comic_num,default=0" json:"comic_num" ` 29 | Sort int `form:"sort" json:"sort" ` 30 | UpdatedAt time.Time `form:"updated_at" json:"updated_at" sql:"-"` 31 | } 32 | -------------------------------------------------------------------------------- /pkg/api/admin/dto/comicComment.go: -------------------------------------------------------------------------------- 1 | package dto 2 | 3 | import "time" 4 | 5 | type ComicCommentListDto struct { 6 | Id int `form:"id" json:"id" ` 7 | Comic_id int `form:"comic_id" json:"comic_id" ` 8 | Username string `form:"username" json:"username" ` 9 | Content string `form:"content,default=0" json:"content" ` 10 | Source string `form:"source" json:"source" ` 11 | CreatedAt time.Time `form:"created_at" json:"created_at"` 12 | UpdatedAt time.Time `form:"updated_at" json:"updated_at"` 13 | DeletedAt *time.Time `form:"deleted_at" json:"deleted_at"` 14 | Page string `form:"page" json:"page" ` 15 | Skip int `form:"skip,default=0" json:"skip"` 16 | Limit int `form:"limit,default=20" json:"limit" binding:"max=100"` 17 | } 18 | 19 | type ComicCommentCreateDto struct { 20 | Comic_id int `form:"comic_id" json:"comic_id" ` 21 | Username string `form:"username" json:"username" ` 22 | Content string `form:"content,default=0" json:"content" ` 23 | Source string `form:"source" json:"source" ` 24 | CreatedAt time.Time `form:"created_at" json:"created_at"` 25 | UpdatedAt time.Time `form:"updated_at" json:"updated_at"` 26 | } 27 | -------------------------------------------------------------------------------- /pkg/api/admin/dto/comment.go: -------------------------------------------------------------------------------- 1 | package dto 2 | 3 | import "time" 4 | 5 | type CommentListDto struct { 6 | Id int `form:"id" json:"id" ` 7 | Name string `form:"name" json:"name" ` 8 | Novel_id int `form:"novel_id" json:"novel_id" ` 9 | Username string `form:"username" json:"username" ` 10 | Content string `form:"content,default=0" json:"content" ` 11 | Likes int `form:"likes" json:"likes" ` 12 | Source string `form:"source" json:"source" ` 13 | CreatedAt time.Time `form:"created_at" json:"created_at"` 14 | UpdatedAt time.Time `form:"updated_at" json:"updated_at"` 15 | DeletedAt *time.Time `form:"deleted_at" json:"deleted_at"` 16 | Page string `form:"page" json:"page" ` 17 | Skip int `form:"skip,default=0" json:"skip"` 18 | Limit int `form:"limit,default=20" json:"limit" binding:"max=100"` 19 | } 20 | 21 | type CommentCreateDto struct { 22 | Novel_id int `form:"novel_id" json:"novel_id" ` 23 | Username string `form:"username" json:"username" ` 24 | Content string `form:"content,default=0" json:"content" ` 25 | Likes int `form:"likes" json:"likes" ` 26 | Source string `form:"source" json:"source" ` 27 | CreatedAt time.Time `form:"created_at" json:"created_at"` 28 | UpdatedAt time.Time `form:"updated_at" json:"updated_at"` 29 | } 30 | -------------------------------------------------------------------------------- /pkg/api/admin/dto/general.go: -------------------------------------------------------------------------------- 1 | package dto 2 | 3 | import "strings" 4 | 5 | // GeneralListDto - General list request params 6 | type GeneralListDto struct { 7 | Skip int `form:"skip,default=0" json:"skip"` 8 | Limit int `form:"limit,default=20" json:"limit" binding:"max=100"` 9 | Order string `form:"order" json:"order"` 10 | Q string `form:"q" json:"q"` 11 | } 12 | 13 | type GeneralTreeDto struct { 14 | Q string `form:"q" json:"q"` 15 | } 16 | type GeneralDelDto struct { 17 | Id int `uri:"id" json:"id" binding:"required"` 18 | } 19 | type GeneralGetDto struct { 20 | Id int `uri:"id" json:"id" binding:"required"` 21 | } 22 | 23 | // TransformSearch - transform search query 24 | func TransformSearch(qs string, mapping map[string]string) (ss map[string]string) { 25 | ss = make(map[string]string) 26 | for _, v := range strings.Split(qs, ",") { 27 | vs := strings.Split(v, "=") 28 | if _, ok := mapping[vs[0]]; ok { 29 | ss[mapping[vs[0]]] = vs[1] 30 | } 31 | } 32 | return 33 | } 34 | -------------------------------------------------------------------------------- /pkg/api/admin/dto/init.go: -------------------------------------------------------------------------------- 1 | package dto 2 | 3 | import ( 4 | "fmt" 5 | "github.com/gin-gonic/gin" 6 | "github.com/pkg/errors" 7 | "gopkg.in/go-playground/validator.v8" 8 | "strings" 9 | ) 10 | 11 | func init() { 12 | 13 | // Register custom validate methods 14 | } 15 | 16 | // Bind : bind request dto and auto verify parameters 17 | func Bind(c *gin.Context, obj interface{}) error { 18 | _ = c.ShouldBindUri(obj) 19 | if err := c.ShouldBind(obj); err != nil { 20 | if fieldErr, ok := err.(validator.ValidationErrors); ok { 21 | var tagErrorMsg []string 22 | for _, v := range fieldErr { 23 | if _, has := ValidateErrorMessage[v.Tag]; has { 24 | tagErrorMsg = append(tagErrorMsg, fmt.Sprintf(ValidateErrorMessage[v.Tag], v.Field, v.Value)) 25 | } else { 26 | tagErrorMsg = append(tagErrorMsg, err.Error()) 27 | } 28 | } 29 | return errors.New(strings.Join(tagErrorMsg, ",")) 30 | } 31 | } 32 | return nil 33 | } 34 | 35 | //ValidateErrorMessage : customize error messages 36 | var ValidateErrorMessage = map[string]string{ 37 | "required": "%s is required,got empty %#v", 38 | } 39 | -------------------------------------------------------------------------------- /pkg/api/admin/dto/upload.go: -------------------------------------------------------------------------------- 1 | package dto 2 | 3 | type UploadTokenDto struct { 4 | key string `json:"key"` 5 | hash string `json:"hash"` 6 | } 7 | -------------------------------------------------------------------------------- /pkg/api/admin/model/base.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | type Pagination struct { 4 | Start int 5 | Limit int 6 | } 7 | -------------------------------------------------------------------------------- /pkg/api/admin/model/book.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "time" 5 | ) 6 | 7 | type Book struct { 8 | Id int `json:"id"` 9 | Name string `json:"name"` 10 | Author string `json:"author"` 11 | Image string `json:"image"` 12 | Status int `son:"status"` 13 | Url string `json:"url"` 14 | Desc string `json:"desc"` 15 | CreateTime time.Time `json:"created_time"` 16 | LastUpdateTime time.Time `json:"updated_time"` 17 | } 18 | 19 | func (d *Book) TableName() string { 20 | return "book" 21 | } 22 | 23 | func init() { 24 | //db.Register(&Book{}) 25 | } 26 | -------------------------------------------------------------------------------- /pkg/api/admin/model/category.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "lime/pkg/common/db" 5 | "time" 6 | ) 7 | 8 | type Category struct { 9 | Id int `json:"id"` 10 | Name string `json:"name"` 11 | ChannelId int `json:"channel_id"` 12 | NovelNum int `json:"novel_num"` 13 | Sort int `json:"sort"` 14 | CreatedAt time.Time `json:"created_at" gorm:"column:created_at;not null;default:CURRENT_TIMESTAMP"` 15 | UpdatedAt time.Time `json:"updated_at" gorm:"column:updated_at;not null;default:CURRENT_TIMESTAMP"` 16 | DeletedAt *time.Time `json:"deleted_at"` 17 | } 18 | 19 | func (C *Category) TableName() string { 20 | return "novel_category" 21 | } 22 | 23 | func init() { 24 | db.Register(&Category{}) 25 | } 26 | -------------------------------------------------------------------------------- /pkg/api/admin/model/chapter.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "time" 5 | ) 6 | 7 | type Chapter struct { 8 | Id int `json:"id"` 9 | Book_id int `json:"book_id"` 10 | Title string `json:"title"` 11 | Content string `json:"content"` 12 | Status int `json:"status"` 13 | Url string `json:"url"` 14 | CreateTime time.Time `json:"created_time"` 15 | LastUpdateTime time.Time `json:"updated_time"` 16 | } 17 | 18 | func (d *Chapter) TableName() string { 19 | return "chapter" 20 | } 21 | 22 | func init() { 23 | //db.Register(&Chapter{}) 24 | } 25 | -------------------------------------------------------------------------------- /pkg/api/admin/model/chapters.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "lime/pkg/common/db" 5 | "time" 6 | ) 7 | 8 | type Chapters struct { 9 | Id int `gorm:"primary_key" json:"id"` //分类ID 10 | Novel_id int `json:"novel_id"` //小说ID 11 | Chapter_no int `json:"chapter_no"` //章节编号 12 | Title string `json:"title"` //章节标题 13 | Desc string `json:"desc"` //章节内容 14 | Link string `json:"link"` //章节采集链接 15 | Is_vip int `json:"is_vip"` //是否收费 16 | Source string `json:"source"` //章节采集站点源 17 | Views int `json:"views"` //浏览次数 18 | Text_num int `json:"text_num"` //章节字数 19 | Status int `json:"status"` //章节采集状态0正常,1失败 20 | Try_views int `json:"try_views"` //采集重试次数 21 | CreatedAt time.Time `json:"created_at" gorm:"column:created_at;not null;default:CURRENT_TIMESTAMP"` 22 | UpdatedAt time.Time `json:"updated_at" gorm:"column:updated_at;not null;default:CURRENT_TIMESTAMP"` 23 | DeletedAt *time.Time `json:"deleted_at"` 24 | } 25 | 26 | func (C *Chapters) TableName() string { 27 | return "novel_chapter_0000" 28 | } 29 | 30 | func init() { 31 | db.Register(&Chapters{}) 32 | } 33 | -------------------------------------------------------------------------------- /pkg/api/admin/model/comicCategory.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "lime/pkg/common/db" 5 | "time" 6 | ) 7 | 8 | type ComicCategory struct { 9 | Id int `json:"id"` 10 | Name string `json:"name"` 11 | Comic_num int `json:"comic_num"` 12 | Sort int `json:"sort"` 13 | CreatedAt time.Time `json:"created_at" gorm:"column:created_at;not null;default:CURRENT_TIMESTAMP"` 14 | UpdatedAt time.Time `json:"updated_at" gorm:"column:updated_at;not null;default:CURRENT_TIMESTAMP"` 15 | DeletedAt *time.Time `json:"deleted_at"` 16 | } 17 | 18 | func (C *ComicCategory) TableName() string { 19 | return "comic_category" 20 | } 21 | 22 | func init() { 23 | db.Register(&ComicCategory{}) 24 | } 25 | -------------------------------------------------------------------------------- /pkg/api/admin/model/comicChapters.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "gorm.io/datatypes" 5 | "time" 6 | ) 7 | 8 | type ComicChapters struct { 9 | Id int `gorm:"primary_key" json:"id"` //分类ID 10 | Comic_id int `json:"comic_id"` //漫画ID 11 | Chapter_no int `json:"chapter_no"` //章节编号 12 | Title string `json:"title"` //章节标题 13 | Desc datatypes.JSON `json:"desc" gorm:"type:json;" ` //章节图 14 | Cover string `json:"cover"` //封面 15 | Upload_type int `json:"upload_type"` //上传类型 1多图上传 2链接上传 16 | Is_vip int `json:"is_vip"` //是否收费 17 | CreatedAt time.Time `json:"created_at" gorm:"column:created_at;not null;default:CURRENT_TIMESTAMP"` 18 | UpdatedAt time.Time `json:"updated_at" gorm:"column:updated_at;not null;default:CURRENT_TIMESTAMP"` 19 | DeletedAt *time.Time `json:"deleted_at"` 20 | } 21 | 22 | type ChapterImage struct { 23 | Name string `json:"name"` 24 | Url string `json:"url"` 25 | } 26 | 27 | func (C *ComicChapters) TableName() string { 28 | return "comic_chapter" 29 | } 30 | 31 | func init() { 32 | //db.Register(&ComicChapters{}) 33 | } 34 | -------------------------------------------------------------------------------- /pkg/api/admin/model/comicComments.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "lime/pkg/common/db" 5 | "time" 6 | ) 7 | 8 | type ComicComments struct { 9 | Id int `gorm:"primary_key" json:"id"` //分类ID 10 | Comic_id int `json:"comic_id"` //漫画ID 11 | Username string `json:"username"` //章节标题 12 | Content string `json:"content"` //内容 13 | Source string `json:"source"` //来源 14 | CreatedAt time.Time `json:"created_at" gorm:"column:created_at;not null;default:CURRENT_TIMESTAMP"` 15 | UpdatedAt time.Time `json:"updated_at" gorm:"column:updated_at;not null;default:CURRENT_TIMESTAMP"` 16 | DeletedAt *time.Time `json:"deleted_at"` 17 | } 18 | 19 | func (C *ComicComments) TableName() string { 20 | return "comic_comment" 21 | } 22 | 23 | func init() { 24 | db.Register(&ComicComments{}) 25 | } 26 | -------------------------------------------------------------------------------- /pkg/api/admin/model/comments.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "lime/pkg/common/db" 5 | "time" 6 | ) 7 | 8 | type Comments struct { 9 | Id int `gorm:"primary_key" json:"id"` //分类ID 10 | Novel_id int `json:"novel_id"` //小说ID 11 | Username string `json:"username"` //章节标题 12 | Content string `json:"content"` //内容 13 | Likes int `json:"likes"` //点赞数 14 | Source string `json:"source"` //来源 15 | CreatedAt time.Time `json:"created_at" gorm:"column:created_at;not null;default:CURRENT_TIMESTAMP"` 16 | UpdatedAt time.Time `json:"updated_at" gorm:"column:updated_at;not null;default:CURRENT_TIMESTAMP"` 17 | DeletedAt *time.Time `json:"deleted_at"` 18 | } 19 | 20 | func (C *Comments) TableName() string { 21 | return "novel_comment" 22 | } 23 | 24 | func init() { 25 | db.Register(&Comments{}) 26 | } 27 | -------------------------------------------------------------------------------- /pkg/api/admin/model/config.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "gorm.io/datatypes" 5 | "time" 6 | ) 7 | 8 | type Config struct { 9 | Id int `gorm:"primary_key" json:"id"` //分类ID 10 | Name string `json:"name"` //名称 11 | Config_code string `json:"config_code"` //配置code 12 | Config_value datatypes.JSON `json:"config_value"` //配置值 13 | Config_group string `json:"config_group"` //组 14 | Desc string `json:"desc"` //描述 15 | Status int `json:"status"` //状态 -1 已经删除 0 禁用 1 启用 16 | CreatedAt time.Time `json:"created_at" gorm:"column:created_at;not null;default:CURRENT_TIMESTAMP"` 17 | UpdatedAt time.Time `json:"updated_at" gorm:"column:updated_at;not null;default:CURRENT_TIMESTAMP"` 18 | DeletedAt *time.Time `json:"deleted_at"` 19 | } 20 | 21 | func (C *Config) TableName() string { 22 | return "config" 23 | } 24 | 25 | func init() { 26 | //db.Register(&Config{}) 27 | } 28 | -------------------------------------------------------------------------------- /pkg/api/admin/model/distributor.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "time" 5 | ) 6 | 7 | type Distributor struct { 8 | Id int `gorm:"primary_key" json:"id"` //分类ID 9 | Recommender_id int `json:"recommender_id"` //推荐人ID 10 | Distributor_id int `json:"distributor_id"` //分销商ID 11 | Name string `json:"name"` //姓名 12 | Mobile string `json:"mobile"` //手机 13 | Distribution_level int `json:"distribution_level"` //分销等级 14 | Commission_withdrawn int `json:"commission_withdrawn"` //已经提现佣金 15 | Commission_available int `json:"commission_available"` //可提现佣金 16 | Status int `json:"status"` //状态:1 已审核 2 待审核 3 已拒绝 17 | CreatedAt time.Time `json:"created_at" gorm:"column:created_at;not null;default:CURRENT_TIMESTAMP"` 18 | UpdatedAt time.Time `json:"updated_at" gorm:"column:updated_at;not null;default:CURRENT_TIMESTAMP"` 19 | DeletedAt *time.Time `json:"deleted_at"` 20 | } 21 | 22 | func (C *Distributor) TableName() string { 23 | return "distributor" 24 | } 25 | 26 | func init() { 27 | //db.Register(&Distributor{}) 28 | } 29 | -------------------------------------------------------------------------------- /pkg/api/admin/model/distributorLevel.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "gorm.io/datatypes" 5 | "time" 6 | ) 7 | 8 | type DistributorLevel struct { 9 | Id int `gorm:"primary_key" json:"id"` //分类ID 10 | Name string `json:"name"` //等级名称 11 | Recommendtype int `json:"recommendtype"` //返佣类型 1按比例返佣 2 按固定返佣 12 | Buyagain_switch int `json:"buyagain_switch"` //复购返佣 13 | Auto_upgrade int `json:"auto_upgrade"` //自动升级 0 否 1是 14 | Upgrade_conditions datatypes.JSON `json:"upgrade_conditions"` //升级条件 15 | Adaptive_degradation int `json:"adaptive_degradation"` //自动降级 16 | Degradation_conditions datatypes.JSON `json:"degradation_conditions"` //降级条件 17 | Weight int `json:"weight"` //权重 18 | CreatedAt time.Time `json:"created_at" gorm:"column:created_at;not null;default:CURRENT_TIMESTAMP"` 19 | UpdatedAt time.Time `json:"updated_at" gorm:"column:updated_at;not null;default:CURRENT_TIMESTAMP"` 20 | DeletedAt *time.Time `json:"deleted_at"` 21 | } 22 | 23 | func (C *DistributorLevel) TableName() string { 24 | return "distributor_level" 25 | } 26 | 27 | func init() { 28 | //db.Register(&DistributorLevel{}) 29 | } 30 | -------------------------------------------------------------------------------- /pkg/api/admin/model/spider.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "github.com/globalsign/mgo/bson" 5 | "time" 6 | ) 7 | 8 | type Spider struct { 9 | Id bson.ObjectId `json:"_id" bson:"_id"` 10 | AppName string `json:"app_name" bson:"app_name"` 11 | CreatedAt time.Time `json:"create_at" bson:"create_at"` 12 | UpdatedAt time.Time `json:"update_at" bson:"update_at"` 13 | } 14 | -------------------------------------------------------------------------------- /pkg/api/admin/service/comicComments.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | log "github.com/sirupsen/logrus" 5 | "lime/pkg/api/admin/dao" 6 | "lime/pkg/api/admin/dto" 7 | "lime/pkg/api/admin/model" 8 | "time" 9 | ) 10 | 11 | var ComicCommentDao = dao.ComicCommentDao{} 12 | 13 | // Service 14 | type ComicCommentervice struct { 15 | } 16 | 17 | // InfoOfId 18 | func (cs ComicCommentervice) InfoOfId(dto dto.GeneralGetDto) model.ComicComments { 19 | return ComicCommentDao.Get(dto.Id) 20 | } 21 | 22 | func (cs ComicCommentervice) GetAll() []model.ComicComments { 23 | return ComicCommentDao.GetAll() 24 | } 25 | 26 | // List 27 | func (cs ComicCommentervice) List(dto dto.ComicCommentListDto) ([]model.ComicComments, int64) { 28 | return ComicCommentDao.List(dto) 29 | } 30 | 31 | // Create 32 | func (cs ComicCommentervice) Create(dto dto.ComicCommentCreateDto) (model.ComicComments, error) { 33 | ComicCommentModel := model.ComicComments{ 34 | Comic_id: dto.Comic_id, 35 | Username: dto.Username, 36 | Content: dto.Content, 37 | Source: dto.Source, 38 | CreatedAt: time.Now(), 39 | UpdatedAt: time.Now(), 40 | } 41 | c := ComicCommentDao.Create(&ComicCommentModel) 42 | if c.Error != nil { 43 | log.Error(c.Error.Error()) 44 | } 45 | return ComicCommentModel, nil 46 | } 47 | 48 | //Delete 49 | func (cs ComicCommentervice) Delete(dto dto.GeneralDelDto) int64 { 50 | ComicCommentModel := model.ComicComments{ 51 | Id: dto.Id, 52 | } 53 | c := ComicCommentDao.Delete(&ComicCommentModel) 54 | return c.RowsAffected 55 | } 56 | -------------------------------------------------------------------------------- /pkg/api/admin/service/comments.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | log "github.com/sirupsen/logrus" 5 | "lime/pkg/api/admin/dao" 6 | "lime/pkg/api/admin/dto" 7 | "lime/pkg/api/admin/model" 8 | "time" 9 | ) 10 | 11 | var CommentDao = dao.CommentDao{} 12 | 13 | // Service 14 | type CommentService struct { 15 | } 16 | 17 | // InfoOfId 18 | func (cs CommentService) InfoOfId(dto dto.GeneralGetDto) model.Comments { 19 | return CommentDao.Get(dto.Id) 20 | } 21 | 22 | func (cs CommentService) GetAll() []model.Comments { 23 | return CommentDao.GetAll() 24 | } 25 | 26 | // List 27 | func (cs CommentService) List(dto dto.CommentListDto) ([]model.Comments, int64) { 28 | return CommentDao.List(dto) 29 | } 30 | 31 | // Create 32 | func (cs CommentService) Create(dto dto.CommentCreateDto) (model.Comments, error) { 33 | CommentModel := model.Comments{ 34 | Novel_id: dto.Novel_id, 35 | Username: dto.Username, 36 | Content: dto.Content, 37 | Likes: dto.Likes, 38 | Source: dto.Source, 39 | CreatedAt: time.Now(), 40 | UpdatedAt: time.Now(), 41 | } 42 | c := CommentDao.Create(&CommentModel) 43 | if c.Error != nil { 44 | log.Error(c.Error.Error()) 45 | } 46 | return CommentModel, nil 47 | } 48 | 49 | //Delete 50 | func (cs CommentService) Delete(dto dto.GeneralDelDto) int64 { 51 | CommentModel := model.Comments{ 52 | Id: dto.Id, 53 | } 54 | c := CommentDao.Delete(&CommentModel) 55 | return c.RowsAffected 56 | } 57 | -------------------------------------------------------------------------------- /pkg/api/admin/service/upload.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "github.com/spf13/viper" 5 | "github.com/qiniu/api.v7/v7/auth/qbox" 6 | "github.com/qiniu/api.v7/v7/storage" 7 | ) 8 | 9 | // Service 10 | type UploadService struct { 11 | } 12 | 13 | func (us UploadService) GetToken() (token string) { 14 | accessKey := viper.GetString("qiniu.ak") 15 | secretKey := viper.GetString("qiniu.sk") 16 | bucket:= viper.GetString("qiniu.bucket") 17 | putPolicy := storage.PutPolicy{ 18 | Scope: bucket, 19 | } 20 | mac := qbox.NewMac(accessKey, secretKey) 21 | return putPolicy.UploadToken(mac) 22 | } 23 | -------------------------------------------------------------------------------- /pkg/api/front/controllers/books.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "lime/pkg/api/admin/controllers" 6 | "lime/pkg/api/front/dto" 7 | "lime/pkg/api/front/service" 8 | "lime/pkg/api/utils/e" 9 | ) 10 | 11 | type BooksController struct { 12 | controllers.BaseController 13 | } 14 | var BooksService = service.BooksService{} 15 | 16 | func (C *BooksController) Get(c *gin.Context) { 17 | var Dto dto.GeneralGetDto 18 | if C.BindAndValidate(c, &Dto) { 19 | data, err := BooksService.GetBookInfoById(Dto) 20 | if err != nil { 21 | C.Fail(c, e.ErrIdData) 22 | return 23 | } 24 | C.Resp(c, map[string]interface{}{ 25 | "result": data, 26 | }) 27 | } 28 | } 29 | 30 | func (C *BooksController) List(c *gin.Context) { 31 | var Dto dto.GeneralListDto 32 | if C.BindAndValidate(c, &Dto) { 33 | data, total := BooksService.List(Dto) 34 | C.Resp(c, map[string]interface{}{ 35 | "result": data, 36 | "total": total, 37 | }) 38 | } 39 | } -------------------------------------------------------------------------------- /pkg/api/front/controllers/chapters.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "lime/pkg/api/admin/controllers" 6 | "lime/pkg/api/front/dto" 7 | "lime/pkg/api/front/service" 8 | "lime/pkg/api/utils/e" 9 | ) 10 | 11 | type ChaptersController struct { 12 | controllers.BaseController 13 | } 14 | var ChaptersService = service.ChaptersService{} 15 | 16 | func (C *ChaptersController) List(c *gin.Context) { 17 | var Dto dto.ChapterListDto 18 | if C.BindAndValidate(c, &Dto) { 19 | data := ChaptersService.List(Dto) 20 | C.Resp(c, map[string]interface{}{ 21 | "result": data, 22 | }) 23 | } 24 | } 25 | 26 | func (C *ChaptersController) Get(c *gin.Context) { 27 | var Dto dto.ChaptersGetDto 28 | if C.BindAndValidate(c, &Dto) { 29 | data,err := ChaptersService.GetChapterInfoById(Dto) 30 | if err != nil { 31 | C.Fail(c, e.ErrIdData) 32 | return 33 | } 34 | C.Resp(c, map[string]interface{}{ 35 | "result": data, 36 | }) 37 | } 38 | } -------------------------------------------------------------------------------- /pkg/api/front/controllers/wechat.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "lime/pkg/api/admin/controllers" 6 | "lime/pkg/api/front/service" 7 | ) 8 | 9 | type WechatController struct { 10 | controllers.BaseController 11 | } 12 | 13 | var WechatService = service.WechatService{} 14 | 15 | func (C *WechatController) Callback(c *gin.Context) { 16 | WechatService.Callback(c) 17 | } -------------------------------------------------------------------------------- /pkg/api/front/dao/books.go: -------------------------------------------------------------------------------- 1 | package dao 2 | 3 | import ( 4 | "fmt" 5 | "lime/pkg/api/front/dto" 6 | "lime/pkg/api/admin/model" 7 | "lime/pkg/common/db" 8 | ) 9 | 10 | type BooksDao struct {} 11 | 12 | func (c BooksDao) Get(id int) model.Books { 13 | var Books model.Books 14 | db := db.GetGormDB() 15 | db.Where("id = ?", id).First(&Books) 16 | return Books 17 | } 18 | 19 | func (c BooksDao) List(listDto dto.GeneralListDto) ([]model.Books, int64) { 20 | var Books []model.Books 21 | var total int64 22 | db := db.GetGormDB() 23 | for sk, sv := range dto.TransformSearch(listDto.Q, dto.BookListSearchMapping) { 24 | db = db.Where(fmt.Sprintf("%s = ?", sk), sv) 25 | } 26 | db.Offset(listDto.Skip).Limit(listDto.Limit).Find(&Books) 27 | db.Model(&model.Books{}).Count(&total) 28 | return Books, total 29 | } 30 | 31 | func (c BooksDao) GetRandBooks(extraId int) []model.Books { 32 | var Books []model.Books 33 | db := db.GetGormDB() 34 | db.Model(&model.Books{}).Where("id !=?",extraId).Order("created_at desc").Limit(2).Find(&Books) 35 | return Books 36 | } -------------------------------------------------------------------------------- /pkg/api/front/dao/category.go: -------------------------------------------------------------------------------- 1 | package dao 2 | 3 | import ( 4 | "lime/pkg/api/admin/model" 5 | "lime/pkg/common/db" 6 | ) 7 | 8 | type CategoryDao struct {} 9 | 10 | 11 | func (c CategoryDao) GetAll() []model.Category { 12 | var Category []model.Category 13 | db := db.GetGormDB() 14 | db.Model(&model.Category{}).Find(&Category) 15 | return Category 16 | } 17 | -------------------------------------------------------------------------------- /pkg/api/front/dao/chapters.go: -------------------------------------------------------------------------------- 1 | package dao 2 | 3 | import ( 4 | "lime/pkg/api/admin/model" 5 | "lime/pkg/common/db" 6 | ) 7 | 8 | type ChaptersDao struct { 9 | } 10 | 11 | func (c ChaptersDao) Get(bookId int,chapterId int) model.Chapters { 12 | var Chapters model.Chapters 13 | db := db.GetGormDB() 14 | db.Where("novel_id = ? and chapter_no = ?", bookId,chapterId).First(&Chapters) 15 | return Chapters 16 | } 17 | 18 | func (c ChaptersDao) GetListByBookId(bookId int) []model.Chapters { 19 | var Chapters []model.Chapters 20 | db := db.GetGormDB() 21 | db.Model(&model.Chapters{}).Where("novel_id = ?",bookId).Find(&Chapters) 22 | return Chapters 23 | } 24 | 25 | func (c ChaptersDao) GetAll() []model.Chapters { 26 | var Chapters []model.Chapters 27 | db := db.GetGormDB() 28 | db.Model(&model.Chapters{}).Find(&Chapters) 29 | return Chapters 30 | } 31 | -------------------------------------------------------------------------------- /pkg/api/front/dao/config.go: -------------------------------------------------------------------------------- 1 | package dao 2 | 3 | import ( 4 | "gorm.io/datatypes" 5 | "lime/pkg/api/admin/model" 6 | "lime/pkg/common/cache" 7 | "lime/pkg/common/db" 8 | ) 9 | 10 | type ConfigDao struct { 11 | } 12 | 13 | func (c ConfigDao) Get(id int) model.Config { 14 | var Config model.Config 15 | db := db.GetGormDB() 16 | db.Where("id = ?", id).First(&Config) 17 | return Config 18 | } 19 | 20 | func (c ConfigDao) GetByCode(code string) datatypes.JSON { 21 | var Config model.Config 22 | configCache, err := cache.Get(code) 23 | if err == nil { 24 | return datatypes.JSON(configCache) 25 | } 26 | db := db.GetGormDB() 27 | db.Where("config_code = ?", code).First(&Config) 28 | cache.Set(code, string(Config.Config_value), 3600) 29 | return Config.Config_value 30 | } 31 | -------------------------------------------------------------------------------- /pkg/api/front/domain/auth/account.go: -------------------------------------------------------------------------------- 1 | package auth 2 | 3 | import ( 4 | "crypto/rand" 5 | "fmt" 6 | "golang.org/x/crypto/scrypt" 7 | "io" 8 | ) 9 | 10 | const pwHashBytes = 64 11 | 12 | // HashPassword : password hashing 13 | func HashPassword(password string, salt string) (hash string, err error) { 14 | h, err := scrypt.Key([]byte(password), []byte(salt), 16384, 8, 1, pwHashBytes) 15 | if err != nil { 16 | return "", err 17 | } 18 | return fmt.Sprintf("%x", h), nil 19 | } 20 | 21 | // MakeSalt : make password more complicated 22 | func MakeSalt() (salt string, err error) { 23 | buf := make([]byte, pwHashBytes) 24 | if _, err := io.ReadFull(rand.Reader, buf); err != nil { 25 | return "", err 26 | } 27 | return fmt.Sprintf("%x", buf), nil 28 | } 29 | -------------------------------------------------------------------------------- /pkg/api/front/domain/auth/login/general.go: -------------------------------------------------------------------------------- 1 | package login 2 | 3 | import ( 4 | "lime/pkg/api/front/domain/auth" 5 | "lime/pkg/api/admin/model" 6 | ) 7 | 8 | // VerifyPassword : verify password by salt 9 | func VerifyPassword(password string, userModel model.Users) bool { 10 | if pwd, err := auth.HashPassword(password, userModel.Salt); err == nil && pwd == userModel.Password { 11 | return true 12 | } 13 | return false 14 | } 15 | -------------------------------------------------------------------------------- /pkg/api/front/domain/wechat/subscribe.go: -------------------------------------------------------------------------------- 1 | package wechat 2 | 3 | import "github.com/silenceper/wechat/v2/officialaccount/message" 4 | 5 | func Subscribe() *message.Reply { 6 | text := message.NewText("欢迎关注lime soft!") 7 | return &message.Reply{MsgType: message.MsgTypeText, MsgData: text} 8 | } 9 | -------------------------------------------------------------------------------- /pkg/api/front/dto/general.go: -------------------------------------------------------------------------------- 1 | package dto 2 | 3 | import "strings" 4 | 5 | // GeneralListDto - General list request params 6 | type GeneralListDto struct { 7 | Skip int `form:"skip,default=0" json:"skip"` 8 | Limit int `form:"limit,default=20" json:"limit" binding:"max=100"` 9 | Order string `form:"order" json:"order"` 10 | Q string `form:"q" json:"q"` 11 | } 12 | 13 | type GeneralTreeDto struct { 14 | Q string `form:"q" json:"q"` 15 | } 16 | type GeneralDelDto struct { 17 | Id int `uri:"id" json:"id" binding:"required"` 18 | } 19 | type GeneralGetDto struct { 20 | Id int `uri:"id" json:"id" binding:"required"` 21 | } 22 | 23 | // TransformSearch - transform search query 24 | func TransformSearch(qs string, mapping map[string]string) (ss map[string]string) { 25 | ss = make(map[string]string) 26 | for _, v := range strings.Split(qs, ",") { 27 | vs := strings.Split(v, "=") 28 | if _, ok := mapping[vs[0]]; ok { 29 | ss[mapping[vs[0]]] = vs[1] 30 | } 31 | } 32 | return 33 | } 34 | -------------------------------------------------------------------------------- /pkg/api/front/dto/init.go: -------------------------------------------------------------------------------- 1 | package dto 2 | 3 | import ( 4 | "fmt" 5 | "github.com/gin-gonic/gin" 6 | "github.com/gin-gonic/gin/binding" 7 | "github.com/go-playground/validator/v10" 8 | "github.com/pkg/errors" 9 | log "github.com/sirupsen/logrus" 10 | "strings" 11 | ) 12 | 13 | func init() { 14 | // Register custom validate methods 15 | if v, ok := binding.Validator.Engine().(*validator.Validate);ok { 16 | _ = v.RegisterValidation("pwdValidate", pwdValidate) 17 | } else { 18 | log.Fatal("Gin fail to registered custom validator(v10)") 19 | } 20 | } 21 | 22 | // Bind : bind request dto and auto verify parameters 23 | func Bind(c *gin.Context, obj interface{}) error { 24 | _ = c.ShouldBindUri(obj) 25 | if err := c.ShouldBind(obj); err != nil { 26 | if fieldErr, ok := err.(validator.ValidationErrors); ok { 27 | var tagErrorMsg []string 28 | for _, v := range fieldErr { 29 | if _, has := ValidateErrorMessage[v.Tag()]; has { 30 | tagErrorMsg = append(tagErrorMsg, fmt.Sprintf(ValidateErrorMessage[v.Tag()], v.Field(), v.Value())) 31 | } else { 32 | tagErrorMsg = append(tagErrorMsg, err.Error()) 33 | } 34 | } 35 | return errors.New(strings.Join(tagErrorMsg, ",")) 36 | } 37 | } 38 | return nil 39 | } 40 | 41 | //ValidateErrorMessage : customize error messages 42 | var ValidateErrorMessage = map[string]string{ 43 | "customValidate": "%s can not be %s", 44 | "required": "%s is required,got empty %#v", 45 | "pwdValidate": "%s is not a valid password", 46 | } 47 | -------------------------------------------------------------------------------- /pkg/api/front/dto/user.go: -------------------------------------------------------------------------------- 1 | package dto 2 | 3 | import ( 4 | "github.com/go-playground/validator/v10" 5 | "regexp" 6 | ) 7 | 8 | type LoginDto struct { 9 | Username string `form:"username" json:"username"` 10 | Password string `form:"password" json:"password"` 11 | } 12 | 13 | type RegisterDto struct { 14 | Username string `json:"username"` 15 | Email string `json:"email"` 16 | Mobile string `json:"mobile"` 17 | Password string `json:"password"` 18 | } 19 | 20 | type LoginInfoDto struct { 21 | AccessToken string `json:"access_token"` 22 | RefreshToken string `json:"refresh_token"` 23 | AtExpires int `json:"at_expires"` 24 | RtExpires int `json:"rt_expires"` 25 | } 26 | 27 | type InfoDto struct { 28 | Id int `json:"id"` 29 | Username string `json:"username"` 30 | Faceicon string `json:"faceicon"` 31 | } 32 | 33 | type BindList struct { 34 | Label string `json:"label"` 35 | Action string `json:"action"` 36 | Status int `json:"status"` 37 | Display string `json:"display"` 38 | } 39 | 40 | type UserEditPasswordDto struct { 41 | Password string `form:"new_password" json:"password" binding:"required,pwdValidate"` 42 | //RePassword string `form:"re_password" json:"re_password" binding:"required,pwdValidate"` 43 | } 44 | 45 | func pwdValidate(fl validator.FieldLevel) bool { 46 | reg := regexp.MustCompile(`^[a-zA-Z0-9!@#$%^&*]{6,}$`) 47 | val := fl.Field().String() 48 | if !reg.Match([]byte(val)) { 49 | return false 50 | } 51 | return true 52 | } 53 | -------------------------------------------------------------------------------- /pkg/api/middleware/auth.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "lime/pkg/api/front/domain/auth" 6 | "net/http" 7 | ) 8 | 9 | func AuthMiddleware() gin.HandlerFunc { 10 | return func(c *gin.Context) { 11 | err := auth.TokenValid(c.Request) 12 | if err != nil { 13 | c.JSON(http.StatusUnauthorized, gin.H{ 14 | "code": http.StatusUnauthorized, 15 | "msg": err.Error(), 16 | }) 17 | c.Abort() 18 | return 19 | } 20 | c.Next() 21 | } 22 | } -------------------------------------------------------------------------------- /pkg/api/middleware/cors.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "github.com/gin-contrib/cors" 5 | "github.com/gin-gonic/gin" 6 | "github.com/spf13/viper" 7 | "time" 8 | ) 9 | 10 | func Cors() gin.HandlerFunc { 11 | return cors.New(cors.Config{ 12 | AllowOrigins: viper.GetStringSlice("cors.allow_origins"), 13 | AllowMethods: viper.GetStringSlice("cors.allow_methods"), 14 | AllowHeaders: viper.GetStringSlice("cors.allow_headers"), 15 | AllowCredentials: viper.GetBool("cors.allow_credentials"), 16 | MaxAge: time.Second * time.Duration(viper.GetInt("cors.max_age")), 17 | }) 18 | } 19 | -------------------------------------------------------------------------------- /pkg/api/middleware/file.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "bytes" 5 | "github.com/gin-gonic/gin" 6 | "io/ioutil" 7 | "net/http" 8 | ) 9 | 10 | //Avoid a large file from loading into memory 11 | //If the file size is greater than 8MB dont allow it to even load into memory and waste our time. 12 | func MaxSizeAllowed(n int64) gin.HandlerFunc { 13 | return func(c *gin.Context) { 14 | c.Request.Body = http.MaxBytesReader(c.Writer, c.Request.Body, n) 15 | buff, errRead := c.GetRawData() 16 | if errRead != nil { 17 | c.JSON(http.StatusRequestEntityTooLarge, gin.H{ 18 | "status": http.StatusRequestEntityTooLarge, 19 | "upload_err": "too large: upload an image less than 8MB", 20 | }) 21 | c.Abort() 22 | return 23 | } 24 | buf := bytes.NewBuffer(buff) 25 | c.Request.Body = ioutil.NopCloser(buf) 26 | } 27 | } 28 | 29 | -------------------------------------------------------------------------------- /pkg/api/router/front.go: -------------------------------------------------------------------------------- 1 | package router 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | controllersFront "lime/pkg/api/front/controllers" 6 | "lime/pkg/api/middleware" 7 | ) 8 | 9 | func Front(e *gin.Engine) { 10 | //前台接口 11 | v1 := e.Group("/v1") 12 | UsersController := &controllersFront.UsersController{} 13 | v1.GET("/user/info",middleware.AuthMiddleware(),UsersController.Info) 14 | v1.POST("/user/login",UsersController.Login) 15 | v1.POST("/user/register",UsersController.Register) 16 | v1.POST("/user/resetPwd",middleware.AuthMiddleware(),UsersController.ResetPassword) 17 | 18 | 19 | V1BooksController := &controllersFront.BooksController{} 20 | v1.GET("/books", V1BooksController.List) 21 | v1.GET("/books/:id", V1BooksController.Get) 22 | 23 | V1ChaptersController := &controllersFront.ChaptersController{} 24 | v1.GET("/chapters", V1ChaptersController.List) 25 | v1.GET("/chapters/:id", V1ChaptersController.Get) 26 | 27 | V1WechatController := &controllersFront.WechatController{} 28 | v1.GET("/wechat/callback", V1WechatController.Callback) 29 | } 30 | -------------------------------------------------------------------------------- /pkg/api/router/router.go: -------------------------------------------------------------------------------- 1 | package router 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "lime/pkg/api/middleware" 6 | "lime/pkg/api/utils/upload" 7 | "net/http" 8 | ) 9 | 10 | func Init(e *gin.Engine, cors bool) { 11 | e.Use( 12 | gin.Recovery(), 13 | ) 14 | if cors { 15 | e.Use(middleware.Cors()) 16 | } 17 | 18 | e.LoadHTMLGlob("./pkg/ui/dist/*.html") // 添加入口index.html 19 | e.LoadHTMLFiles("./pkg/ui/dist/static/*/*") // 添加资源路径 20 | e.Static("/static", "./pkg/ui/dist/static") // 添加资源路径 21 | e.StaticFile("/admin/", "./pkg/ui/dist/index.html") //前端接口 22 | e.StaticFS("/upload/images", http.Dir(upload.GetImageFullPath())) 23 | e.StaticFS("/upload/books", http.Dir("data/books/")) 24 | } 25 | -------------------------------------------------------------------------------- /pkg/api/utils/e/code.go: -------------------------------------------------------------------------------- 1 | package e 2 | 3 | type ControllerError struct { 4 | Code int `json:"code"` 5 | Message string `json:"msg"` 6 | Moreinfo string `json:"moreinfo"` 7 | } 8 | 9 | var ( 10 | Err404 = &ControllerError{404, "页面没有找到", ""} 11 | ErrInputData = &ControllerError{10001, "数据输入错误", ""} 12 | ErrDatabase = &ControllerError{10002, "服务器错误", ""} 13 | ErrValidation = &ControllerError{13011, "请求参数验证失败", ""} 14 | ErrAddFail = &ControllerError{11000, "创建失败", ""} 15 | ErrEditFail = &ControllerError{11001, "更新失败", ""} 16 | ErrDelFail = &ControllerError{11002, "删除失败", ""} 17 | ErrInvalidParams = &ControllerError{11003, "验证失败", ""} 18 | ErrIdData = &ControllerError{10016, "此ID无数据记录", ""} 19 | OtherTaskRunning = &ControllerError{12000, "有其他任务在执行", ""} 20 | ErrUploadCover = &ControllerError{12001, "上传封面失败", ""} 21 | ErrLogin = &ControllerError{12002, "登陆失败,请输入正确的用户名和密码", ""} 22 | ErrReg = &ControllerError{12003, "注册失败", ""} 23 | ErrUnauthorized = &ControllerError{12004, "校验身份失败,请重新登陆", ""} 24 | ) 25 | -------------------------------------------------------------------------------- /pkg/common/cache/init.go: -------------------------------------------------------------------------------- 1 | package cache 2 | 3 | import "time" 4 | 5 | var adapter Adapter 6 | 7 | type Adapter interface { 8 | Connect() 9 | Get(key string) (string, error) 10 | Set(key string, val string, expire int) error 11 | Del(key string) error 12 | HashGet(hk, key string) (string, error) 13 | HashDel(hk, key string) error 14 | Increase(key string) error 15 | Expire(key string, dur time.Duration) error 16 | } 17 | 18 | func SetUp() { 19 | adapter = &Redis{} 20 | adapter.Connect() 21 | } 22 | 23 | // Set val in cache 24 | func Set(key, val string, expire int) error { 25 | return adapter.Set(key, val, expire) 26 | } 27 | 28 | // Get val in cache 29 | func Get(key string) (string, error) { 30 | return adapter.Get(key) 31 | } 32 | 33 | // Del delete key in cache 34 | func Del(key string) error { 35 | return adapter.Del(key) 36 | } 37 | 38 | // HashGet get val in hashtable cache 39 | func HashGet(hk, key string) (string, error) { 40 | return adapter.HashGet(hk, key) 41 | } 42 | 43 | // HashDel delete one key:value pair in hashtable cache 44 | func HashDel(hk, key string) error { 45 | return adapter.HashDel(hk, key) 46 | } 47 | 48 | // Increase value 49 | func Increase(key string) error { 50 | return adapter.Increase(key) 51 | } 52 | 53 | func Expire(key string, dur time.Duration) error { 54 | return adapter.Expire(key, dur) 55 | } 56 | -------------------------------------------------------------------------------- /pkg/common/db/db_test.go: -------------------------------------------------------------------------------- 1 | package db 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | sqlmock "github.com/DATA-DOG/go-sqlmock" 8 | "github.com/jinzhu/gorm" 9 | "github.com/stretchr/testify/assert" 10 | "github.com/stretchr/testify/require" 11 | ) 12 | 13 | type testModel struct { 14 | ID uint64 `gorm:"column:id;type:bigint unsigned AUTO_INCREMENT;primary_key"` 15 | Name string `gorm:"column:user_name;type:varchar(32) not null"` 16 | } 17 | 18 | func (t *testModel) TableName() string { 19 | return "testmodel" 20 | } 21 | 22 | func TestRegisterAndAutoMigrate(t *testing.T) { 23 | assert.Panics(t, func() { 24 | Register(nil) 25 | }) 26 | assert.Panics(t, func() { 27 | var tm *testModel 28 | Register(tm) 29 | }) 30 | 31 | Register(&testModel{}) 32 | assert.Panics(t, func() { 33 | Register(&testModel{}) 34 | }) 35 | 36 | db, mock, err := sqlmock.New() 37 | require.NoError(t, err) 38 | defer db.Close() 39 | gdb, err := gorm.Open("mysql", db) 40 | require.NoError(t, err) 41 | SetGormDB(gdb) 42 | 43 | mock.ExpectExec("(?i)create table `gospider_testmodel`"). 44 | WillReturnResult(sqlmock.NewResult(0, 0)) 45 | 46 | assert.NoError(t, AutoMigrate()) 47 | assert.NoError(t, mock.ExpectationsWereMet()) 48 | 49 | mock.ExpectExec("(?i)create table `gospider_testmodel`"). 50 | WillReturnError(fmt.Errorf("some error")) 51 | assert.NotNil(t, AutoMigrate()) 52 | assert.NoError(t, mock.ExpectationsWereMet()) 53 | 54 | } 55 | -------------------------------------------------------------------------------- /pkg/common/db/mongo_test.go: -------------------------------------------------------------------------------- 1 | package db 2 | 3 | import ( 4 | . "github.com/smartystreets/goconvey/convey" 5 | "github.com/spf13/viper" 6 | "testing" 7 | ) 8 | 9 | func TestGetDb(t *testing.T) { 10 | Convey("Test GetDb", t, func() { 11 | s, db := GetDb() 12 | Convey("The value should be Session.Copy", func() { 13 | So(s, ShouldEqual, Session.Copy()) 14 | }) 15 | Convey("The value should be reference of database", func() { 16 | So(db, ShouldEqual, s.DB(viper.GetString("mongo.db"))) 17 | }) 18 | }) 19 | } 20 | -------------------------------------------------------------------------------- /pkg/common/types.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | const ( 4 | OutputTypeMySQL = "mysql" 5 | OutputTypeCSV = "csv" 6 | OutputDbBook = "book" 7 | OutputMongodb = "mongodb" 8 | OutputYaml = "yaml" 9 | ) 10 | 11 | type MTS struct { 12 | ID int 13 | Status TaskStatus 14 | } 15 | -------------------------------------------------------------------------------- /pkg/crawler/core/task.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "github.com/pkg/errors" 7 | "sync" 8 | ) 9 | 10 | // Task is a task define 11 | type Task struct { 12 | ID int 13 | TaskRule 14 | TaskConfig 15 | } 16 | 17 | // NewTask return a new task object 18 | func NewTask(id int, rule TaskRule, config TaskConfig) *Task { 19 | return &Task{ 20 | ID: id, 21 | TaskRule: rule, 22 | TaskConfig: config, 23 | } 24 | } 25 | 26 | var ( 27 | ctlMu = &sync.RWMutex{} 28 | ctlMap = make(map[int]context.CancelFunc) 29 | ) 30 | 31 | func addTaskCtrl(taskID int, cancelFunc context.CancelFunc) error { 32 | ctlMu.Lock() 33 | defer ctlMu.Unlock() 34 | 35 | if _, ok := ctlMap[taskID]; ok { 36 | return errors.WithStack(fmt.Errorf("duplicate taskID:%d", taskID)) 37 | } 38 | ctlMap[taskID] = cancelFunc 39 | 40 | return nil 41 | } 42 | 43 | // CancelTask cancel a task by taskID 44 | func CancelTask(taskID int) bool { 45 | ctlMu.Lock() 46 | defer ctlMu.Unlock() 47 | 48 | cancel, ok := ctlMap[taskID] 49 | if !ok { 50 | return false 51 | } 52 | cancel() 53 | delete(ctlMap, taskID) 54 | 55 | return true 56 | } 57 | -------------------------------------------------------------------------------- /pkg/crawler/novels/aoyuge/file.go: -------------------------------------------------------------------------------- 1 | package aoyuge 2 | 3 | import ( 4 | "fmt" 5 | "github.com/go-yaml/yaml" 6 | "io/ioutil" 7 | "net/url" 8 | "os" 9 | "sync" 10 | ) 11 | 12 | // Chapter 章节 13 | type Chapter struct { 14 | Name string //名称 15 | URL string //章节链接 16 | Text []string 17 | Alias []string `yaml:"-"` 18 | MuxLock sync.Mutex `yaml:"-"` 19 | } 20 | 21 | const FileExt = "lime" 22 | 23 | // Volume 卷 24 | type Volume struct { 25 | Name string 26 | IsVIP bool 27 | Chapters []Chapter 28 | } 29 | 30 | // Store is store yaml data file format 31 | type Store struct { 32 | BookURL string 33 | BookName string 34 | Author string // 作者 35 | CoverURL string // 封面链接 36 | Description string // 介绍 37 | Volumes []Volume 38 | } 39 | 40 | func WriteBook(bookurl string, chapter Store) error { 41 | bookURL, err := url.Parse(bookurl) 42 | if err != nil { 43 | return err 44 | } 45 | chapter.BookURL = bookURL.String() 46 | filename := fmt.Sprintf("%s.%s", chapter.BookName, FileExt) 47 | filemode, err := os.Stat(filename) 48 | if err != nil && os.IsNotExist(err) { 49 | bookContent, err := yaml.Marshal(chapter) 50 | if err != nil { 51 | return err 52 | } 53 | ioutil.WriteFile(filename, bookContent, 0775) 54 | return nil 55 | } 56 | if filemode.IsDir() { 57 | return fmt.Errorf("is Dir") 58 | } 59 | return nil 60 | } 61 | -------------------------------------------------------------------------------- /pkg/crawler/novels/biquge/book.go: -------------------------------------------------------------------------------- 1 | package biquge 2 | 3 | import ( 4 | "fmt" 5 | "github.com/gocolly/colly" 6 | "lime/pkg/crawler/novels" 7 | //"regexp" 8 | ) 9 | 10 | var rules = novels.GetRules().RuleConfigInfo 11 | 12 | func Book() { 13 | c := novels.Init() 14 | c.DetectCharset = true 15 | c.MaxDepth = 1 16 | c.OnHTML("html", func(e *colly.HTMLElement) { 17 | //intro,_ := e.DOM.Find("#maininfo").Html() 18 | //re, _ := regexp.Compile(rules.NovelIntro.Pattern) 19 | //NovelIntro := re.FindString(intro) 20 | //fmt.Println(NovelIntro) 21 | }) 22 | c.OnRequest(func(r *colly.Request) { 23 | fmt.Println("Visiting", r.URL.String()) 24 | }) 25 | c.Visit("https://www.xbaquge.com/files/article/html/40/40670/") 26 | } 27 | -------------------------------------------------------------------------------- /pkg/crawler/novels/biquge/chapter.go: -------------------------------------------------------------------------------- 1 | package biquge 2 | 3 | import ( 4 | "fmt" 5 | "github.com/gocolly/colly" 6 | "lime/pkg/api/admin/dto" 7 | "lime/pkg/api/admin/service" 8 | "lime/pkg/crawler/novels" 9 | ) 10 | 11 | var ChapterService = service.ChapterService{} 12 | 13 | func Chapter() { 14 | c := novels.Init() 15 | c.DetectCharset = true 16 | c.MaxDepth = 1 17 | c.OnHTML("a[href]", func(e *colly.HTMLElement) { 18 | url_prefix := e.Request.URL.Scheme + "://" + e.Request.URL.Host 19 | link := e.Attr("href") 20 | title := e.Text 21 | var chapterDto dto.ChapterCreateDto 22 | chapterDto.Url = url_prefix + link 23 | chapterDto.Book_id = 3 24 | chapterDto.Title = title 25 | created := ChapterService.Create(chapterDto) 26 | if created.Id <= 0 { 27 | fmt.Println("error add") 28 | } 29 | }) 30 | c.OnRequest(func(r *colly.Request) { 31 | fmt.Println("Visiting", r.URL.String()) 32 | }) 33 | c.Visit("https://www.xbaquge.com/files/article/html/40/40670/") 34 | } 35 | -------------------------------------------------------------------------------- /pkg/crawler/novels/biquge/list.go: -------------------------------------------------------------------------------- 1 | package biquge 2 | 3 | import ( 4 | "fmt" 5 | "lime/pkg/crawler/novels" 6 | ) 7 | 8 | //采集列表页面 9 | 10 | func Test() { 11 | rules := novels.GetRules() 12 | fmt.Println(rules.RuleConfigInfo.GetSiteUrl.Pattern) 13 | } 14 | -------------------------------------------------------------------------------- /pkg/crawler/novels/store.go: -------------------------------------------------------------------------------- 1 | package novels 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "lime/pkg/api/admin/model" 7 | "lime/pkg/common/db" 8 | "time" 9 | ) 10 | 11 | const FileExt = "crawnovel" 12 | 13 | func SaveBook(row map[int]interface{}) error { 14 | dto := model.Book{} 15 | dto.Name = fmt.Sprintf("%v", row[0]) 16 | dto.Author = fmt.Sprintf("%v", row[1]) 17 | dto.Image = fmt.Sprintf("%v", row[2]) 18 | dto.Url = fmt.Sprintf("%v", row[3]) 19 | dto.Desc = fmt.Sprintf("%v", row[4]) 20 | dto.Status = 0 21 | dto.CreateTime = time.Now() 22 | dto.LastUpdateTime = time.Now() 23 | db := db.GetGormDB() 24 | created := db.Save(&dto) 25 | if created.RowsAffected > 0 { 26 | return nil 27 | } 28 | return errors.New("add book error") 29 | 30 | } 31 | -------------------------------------------------------------------------------- /pkg/down/convert.go: -------------------------------------------------------------------------------- 1 | package down 2 | 3 | import ( 4 | "fmt" 5 | log "github.com/sirupsen/logrus" 6 | "lime/pkg/down/output" 7 | ) 8 | 9 | var outputOpt output.Option 10 | 11 | func Covert(filename string, format string, outputpath string) error { 12 | if err := LoadLocalStore(filename); err != nil { 13 | return err 14 | } 15 | if format == "" { 16 | return nil 17 | } 18 | var ConversionFileName string 19 | if outputpath == "" { 20 | ConversionFileName = fmt.Sprintf("%s.%s", chapter.BookName, format) 21 | } 22 | log.Printf("Start Conversion: Format:%#v OutPath:%#v", format, ConversionFileName) 23 | return output.Output(*chapter, format, ConversionFileName, outputOpt) 24 | } 25 | -------------------------------------------------------------------------------- /pkg/down/job.go: -------------------------------------------------------------------------------- 1 | package down 2 | 3 | import ( 4 | log "github.com/sirupsen/logrus" 5 | "io" 6 | "lime/pkg/down/site" 7 | "time" 8 | ) 9 | 10 | var ( 11 | tSleep time.Duration 12 | errSleep time.Duration 13 | ) 14 | 15 | func Job(syncStore *SyncStore, jobch chan error) { 16 | defer func(jobch chan error) { 17 | jobch <- io.EOF 18 | }(jobch) 19 | for { 20 | vi, ci, BookURL, err := syncStore.GetJob() 21 | if err != nil { 22 | if err != io.EOF { 23 | jobch <- err 24 | } 25 | return 26 | } 27 | 28 | LoopStep: 29 | for { 30 | content, err := site.Chapter(BookURL) 31 | if err != nil { 32 | log.Printf("Error: %s", err) 33 | time.Sleep(errSleep) 34 | continue LoopStep 35 | } 36 | syncStore.SaveJob(vi, ci, content) 37 | jobch <- nil 38 | time.Sleep(tSleep) 39 | break LoopStep 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /pkg/down/output/output.go: -------------------------------------------------------------------------------- 1 | package output 2 | 3 | import ( 4 | "fmt" 5 | "lime/pkg/down/store" 6 | "reflect" 7 | ) 8 | 9 | func init() { 10 | RegOutputFormat("epub", &EPUB{}) 11 | RegOutputFormat("md", &Markdown{}) 12 | RegOutputFormat("txt", &TXT{}) 13 | } 14 | 15 | // Option is Convert output options 16 | type Option struct { 17 | IgnoreCover bool // 忽略封面 18 | NoEPUBMetadata bool // 不添加EPUB元数据 19 | } 20 | 21 | type Conversion interface { 22 | Conv(src store.Store, outpath string, opts Option) error 23 | } 24 | 25 | var formatMap = map[string]Conversion{} 26 | 27 | var ( 28 | ErrUnsupportFormat = fmt.Errorf("Unsupport Conversion Format") 29 | ) 30 | 31 | func RegOutputFormat(s string, conv Conversion) { 32 | formatMap[s] = conv 33 | } 34 | 35 | func Output(src store.Store, format string, outpath string, opts Option) (err error) { 36 | var c Conversion 37 | conver, ok := formatMap[format] 38 | if !ok { 39 | err = ErrUnsupportFormat 40 | } 41 | c = reflect.New(reflect.TypeOf(conver).Elem()).Interface().(Conversion) 42 | return c.Conv(src, outpath, opts) 43 | } 44 | -------------------------------------------------------------------------------- /pkg/down/output/txt.go: -------------------------------------------------------------------------------- 1 | package output 2 | 3 | import ( 4 | "html/template" 5 | "lime/pkg/down/store" 6 | "os" 7 | "strings" 8 | ) 9 | 10 | type TXT struct { 11 | } 12 | 13 | func (t *TXT) Conv(src store.Store, outpath string, opts Option) (err error) { 14 | f, err := os.Create(outpath) 15 | if err != nil { 16 | return err 17 | } 18 | defer f.Close() 19 | 20 | temp := template.New("txt_lime") 21 | temp = temp.Funcs(template.FuncMap{ 22 | "split": strings.Split, 23 | }) 24 | temp, err = temp.Parse(TxtTemplate) 25 | if err != nil { 26 | return err 27 | } 28 | 29 | return temp.Execute( 30 | f, src) 31 | } 32 | 33 | var TxtTemplate = `书名:{{.BookName}} 34 | 作者:{{.Author}} 35 | 链接:{{.BookURL}} 36 | 简介: 37 | {{range split .Description "\n"}} {{.}} 38 | {{end}} 39 | {{- range .Volumes }} 40 | {{if .IsVIP}}付费{{else}}免费{{end}}卷 {{.Name}} 41 | {{range .Chapters}} 42 | {{.Name}} 43 | {{range .Text}} {{.}} 44 | {{end}}{{end}}{{end}}` 45 | -------------------------------------------------------------------------------- /pkg/down/search.go: -------------------------------------------------------------------------------- 1 | package down 2 | 3 | import ( 4 | "fmt" 5 | "github.com/go-yaml/yaml" 6 | log "github.com/sirupsen/logrus" 7 | "io/ioutil" 8 | "lime/pkg/down/site" 9 | ) 10 | 11 | func Search(keyword string, put bool, filename string, driver string) error { 12 | var bookurl = "" 13 | r, err := site.Search(keyword) 14 | if err != nil { 15 | return err 16 | } 17 | if !put { 18 | fmt.Printf("搜索到%d个内容:\n", len(r)) 19 | for _, v := range r { 20 | fmt.Printf("%s %s %s\n", v.BookURL, v.BookName, v.Author) 21 | } 22 | return nil 23 | } 24 | if filename == "" { 25 | err := InitLoadStore(driver, bookurl) 26 | if err != nil { 27 | return err 28 | } 29 | } else { 30 | err := LoadLocalStore(filename) 31 | if err != nil { 32 | return err 33 | } 34 | } 35 | rrr := []site.ChaperSearchResult{} 36 | for _, v := range r { 37 | if (v.Author == chapter.Author) && (v.BookName == chapter.BookName) { 38 | log.Printf("%s %s %s", v.BookURL, v.BookName, v.Author) 39 | rrr = append(rrr, v) 40 | } 41 | } 42 | b, err := yaml.Marshal(chapter) 43 | if err != nil { 44 | return err 45 | } 46 | ioutil.WriteFile(filename, b, 0775) 47 | return nil 48 | } 49 | -------------------------------------------------------------------------------- /pkg/down/site/jjxs.go: -------------------------------------------------------------------------------- 1 | package site 2 | 3 | var jyxs = SiteA{ 4 | Name: "精英小说", 5 | HomePage: "http://www.jyyxs.com/", 6 | Match: []string{ 7 | `https://www\.jyyxs\.com/\d+_\d+/*`, 8 | `https://www\.jyyxs\.com/\d+_\d+/\d+\.html/*`, 9 | }, 10 | } 11 | -------------------------------------------------------------------------------- /pkg/down/site/site_chromedp.go: -------------------------------------------------------------------------------- 1 | package site 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "github.com/chromedp/chromedp" 7 | "lime/pkg/down/store" 8 | "log" 9 | "net/url" 10 | "strings" 11 | ) 12 | 13 | func ChromedpBookInfo(BookURL string, logfile string) (s *store.Store, err error) { 14 | 15 | ms, err := MatchOne(Sitepool, BookURL) 16 | if err != nil { 17 | return nil, err 18 | } 19 | 20 | var ( 21 | // BookName string 22 | // Author string 23 | html string 24 | u *url.URL 25 | ) 26 | 27 | u, _ = url.Parse(BookURL) 28 | 29 | tasks := chromedp.Tasks{ 30 | chromedp.Navigate(BookURL), 31 | // chromedp.Text(`html`, &html, chromedp.ByQuery), 32 | chromedp.OuterHTML(`html`, &html, chromedp.ByQuery), 33 | // chromedp.WaitVisible(`html`, chromedp.ByQuery), 34 | } 35 | 36 | // create chrome instance 37 | ctx, cancel := chromedp.NewContext(context.Background()) 38 | defer cancel() 39 | 40 | if err := chromedp.Run(ctx, tasks...); err != nil { 41 | log.Fatal(err) 42 | } 43 | 44 | chapter, err := ms.BookInfo(strings.NewReader(html)) 45 | if err != nil { 46 | return nil, err 47 | } 48 | 49 | for v1, k1 := range chapter.Volumes { 50 | for v2, k2 := range k1.Chapters { 51 | u1, _ := url.Parse(k2.URL) 52 | chapter.Volumes[v1].Chapters[v2].URL = u.ResolveReference(u1).String() 53 | } 54 | } 55 | 56 | if len(chapter.Volumes) == 0 { 57 | // fmt.Printf(content) 58 | return nil, fmt.Errorf("not match volumes") 59 | } 60 | 61 | return chapter, nil 62 | } 63 | -------------------------------------------------------------------------------- /pkg/down/store/store.go: -------------------------------------------------------------------------------- 1 | package store 2 | 3 | import "sync" 4 | 5 | const FileExt = "lime" 6 | 7 | // Volume 卷 8 | type Volume struct { 9 | Name string 10 | IsVIP bool 11 | Chapters []Chapter 12 | } 13 | 14 | // Chapter 章节 15 | type Chapter struct { 16 | Name string //名称 17 | URL string //章节链接 18 | Text []string 19 | Alias []string `yaml:"-"` 20 | MuxLock sync.Mutex `yaml:"-"` 21 | } 22 | 23 | // Store is store yaml data file format 24 | type Store struct { 25 | BookURL string 26 | BookName string 27 | Author string // 作者 28 | CoverURL string // 封面链接 29 | Description string // 介绍 30 | Volumes []Volume 31 | } 32 | 33 | func (store Store) Total() (Done, AllChaper int) { 34 | for _, v := range store.Volumes { 35 | AllChaper += len(v.Chapters) 36 | for _, v2 := range v.Chapters { 37 | if len(v2.Text) != 0 { 38 | Done++ 39 | } 40 | } 41 | } 42 | return 43 | } 44 | -------------------------------------------------------------------------------- /pkg/down/syncstore.go: -------------------------------------------------------------------------------- 1 | package down 2 | 3 | import ( 4 | "fmt" 5 | "github.com/go-yaml/yaml" 6 | "io" 7 | "io/ioutil" 8 | "lime/pkg/down/store" 9 | "sync" 10 | ) 11 | 12 | type SyncStore struct { 13 | lock *sync.Mutex 14 | IsTWork bool 15 | jobs [][]bool 16 | Store *store.Store 17 | } 18 | 19 | func (s *SyncStore) Init() { 20 | s.jobs = make([][]bool, len(s.Store.Volumes)) 21 | s.lock = &sync.Mutex{} 22 | for k := range s.jobs { 23 | s.jobs[k] = make([]bool, len(s.Store.Volumes[k].Chapters)) 24 | } 25 | } 26 | 27 | func (s *SyncStore) GetJob() (vi, ci int, url string, err error) { 28 | s.lock.Lock() 29 | defer s.lock.Unlock() 30 | for vi, vol := range s.Store.Volumes { 31 | for ci, ch := range vol.Chapters { 32 | if !s.jobs[vi][ci] { 33 | if len(ch.Text) == 0 { 34 | s.jobs[vi][ci] = true 35 | return vi, ci, ch.URL, nil 36 | } 37 | } 38 | } 39 | } 40 | return 0, 0, "", io.EOF 41 | } 42 | 43 | func (s *SyncStore) SaveJob(vi, ci int, text []string) { 44 | s.lock.Lock() 45 | defer s.lock.Unlock() 46 | s.Store.Volumes[vi].Chapters[ci].Text = text 47 | bbb, err := yaml.Marshal(*(s.Store)) 48 | if err != nil { 49 | panic(err) 50 | } 51 | var filename = fmt.Sprintf("%s.%s", s.Store.BookName, store.FileExt) 52 | err = ioutil.WriteFile(filename, bbb, 0775) 53 | if err != nil { 54 | panic(err) 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /pkg/down/util.go: -------------------------------------------------------------------------------- 1 | package down 2 | 3 | import ( 4 | "regexp" 5 | ) 6 | 7 | func TitleAlias(s string) (alias []string) { 8 | var res = []string{ 9 | `^([第]?([零一二三四五六七八就十百千万0-9]+)[章\,,、]?)[ ]*([^ ()\(\))]+).*$`, 10 | `([^ ()\(\)]+)`, // 没有章节号的章节 11 | `[(\(](.+?)[)\)]`, //括号内的 12 | } 13 | for _, v := range res { 14 | re, err := regexp.Compile(v) 15 | if err != nil { 16 | panic(err) 17 | } 18 | find := re.FindAllStringSubmatch(s, -1) 19 | if find == nil { 20 | continue 21 | } 22 | for _, v1 := range find { 23 | for _, v := range v1[1:] { 24 | if v == "" || v == s || StringInSlice(v, alias) { 25 | continue 26 | } 27 | alias = append(alias, v) 28 | } 29 | } 30 | } 31 | return 32 | } 33 | 34 | func StringInSlice(str string, slice []string) bool { 35 | for _, v := range slice { 36 | if str == v { 37 | return true 38 | } 39 | } 40 | return false 41 | } 42 | -------------------------------------------------------------------------------- /pkg/h5/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["env", { 4 | "modules": false, 5 | "targets": { 6 | "browsers": ["> 1%", "last 2 versions", "not ie <= 8"] 7 | } 8 | }], 9 | "stage-2" 10 | ], 11 | "plugins": ["transform-vue-jsx", "transform-runtime"], 12 | "env": { 13 | "test": { 14 | "presets": ["env", "stage-2"], 15 | "plugins": ["transform-vue-jsx", "istanbul"] 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /pkg/h5/.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 4 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /pkg/h5/.eslintignore: -------------------------------------------------------------------------------- 1 | /build/ 2 | /config/ 3 | /dist/ 4 | /*.js 5 | /test/unit/coverage/ 6 | -------------------------------------------------------------------------------- /pkg/h5/.eslintrc.js: -------------------------------------------------------------------------------- 1 | // https://eslint.org/docs/user-guide/configuring 2 | 3 | module.exports = { 4 | root: true, 5 | parserOptions: { 6 | parser: 'babel-eslint' 7 | }, 8 | env: { 9 | browser: true, 10 | }, 11 | extends: [ 12 | // https://github.com/vuejs/eslint-plugin-vue#priority-a-essential-error-prevention 13 | // consider switching to `plugin:vue/strongly-recommended` or `plugin:vue/recommended` for stricter rules. 14 | 'plugin:vue/essential', 15 | // https://github.com/standard/standard/blob/master/docs/RULES-en.md 16 | 'standard' 17 | ], 18 | // required to lint *.vue files 19 | plugins: [ 20 | 'vue' 21 | ], 22 | // add your custom rules here 23 | rules: { 24 | // allow async-await 25 | 'generator-star-spacing': 'off', 26 | // allow debugger during development 27 | 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off' 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /pkg/h5/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | /dist/ 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | /test/ 8 | selenium-debug.log 9 | 10 | # Editor directories and files 11 | .idea 12 | .vscode 13 | *.suo 14 | *.ntvs* 15 | *.njsproj 16 | *.sln 17 | -------------------------------------------------------------------------------- /pkg/h5/.postcssrc.js: -------------------------------------------------------------------------------- 1 | // https://github.com/michael-ciniawsky/postcss-load-config 2 | 3 | module.exports = { 4 | "plugins": { 5 | "postcss-import": {}, 6 | "postcss-url": {}, 7 | // to edit target browsers: use "browserslist" field in package.json 8 | "autoprefixer": {} 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /pkg/h5/README.md: -------------------------------------------------------------------------------- 1 | H5 阅读 2 | 3 | 4 | 特别鸣谢: 5 | 6 | 本H5项目查考: [https://github.com/YepFury/reader](https://github.com/YepFury/reader) -------------------------------------------------------------------------------- /pkg/h5/build/build.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | require('./check-versions')() 3 | 4 | process.env.NODE_ENV = 'production' 5 | 6 | const ora = require('ora') 7 | const rm = require('rimraf') 8 | const path = require('path') 9 | const chalk = require('chalk') 10 | const webpack = require('webpack') 11 | const config = require('../config') 12 | const webpackConfig = require('./webpack.prod.conf') 13 | 14 | const spinner = ora('building for production...') 15 | spinner.start() 16 | 17 | rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => { 18 | if (err) throw err 19 | webpack(webpackConfig, (err, stats) => { 20 | spinner.stop() 21 | if (err) throw err 22 | process.stdout.write(stats.toString({ 23 | colors: true, 24 | modules: false, 25 | children: false, // If you are using ts-loader, setting this to true will make TypeScript errors show up during build. 26 | chunks: false, 27 | chunkModules: false 28 | }) + '\n\n') 29 | 30 | if (stats.hasErrors()) { 31 | console.log(chalk.red(' Build failed with errors.\n')) 32 | process.exit(1) 33 | } 34 | 35 | console.log(chalk.cyan(' Build complete.\n')) 36 | console.log(chalk.yellow( 37 | ' Tip: built files are meant to be served over an HTTP server.\n' + 38 | ' Opening index.html over file:// won\'t work.\n' 39 | )) 40 | }) 41 | }) 42 | -------------------------------------------------------------------------------- /pkg/h5/build/check-versions.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const chalk = require('chalk') 3 | const semver = require('semver') 4 | const packageConfig = require('../package.json') 5 | const shell = require('shelljs') 6 | 7 | function exec (cmd) { 8 | return require('child_process').execSync(cmd).toString().trim() 9 | } 10 | 11 | const versionRequirements = [ 12 | { 13 | name: 'node', 14 | currentVersion: semver.clean(process.version), 15 | versionRequirement: packageConfig.engines.node 16 | } 17 | ] 18 | 19 | if (shell.which('npm')) { 20 | versionRequirements.push({ 21 | name: 'npm', 22 | currentVersion: exec('npm --version'), 23 | versionRequirement: packageConfig.engines.npm 24 | }) 25 | } 26 | 27 | module.exports = function () { 28 | const warnings = [] 29 | 30 | for (let i = 0; i < versionRequirements.length; i++) { 31 | const mod = versionRequirements[i] 32 | 33 | if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) { 34 | warnings.push(mod.name + ': ' + 35 | chalk.red(mod.currentVersion) + ' should be ' + 36 | chalk.green(mod.versionRequirement) 37 | ) 38 | } 39 | } 40 | 41 | if (warnings.length) { 42 | console.log('') 43 | console.log(chalk.yellow('To use this template, you must update following to modules:')) 44 | console.log() 45 | 46 | for (let i = 0; i < warnings.length; i++) { 47 | const warning = warnings[i] 48 | console.log(' ' + warning) 49 | } 50 | 51 | console.log() 52 | process.exit(1) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /pkg/h5/build/vue-loader.conf.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const utils = require('./utils') 3 | const config = require('../config') 4 | const isProduction = process.env.NODE_ENV === 'production' 5 | const sourceMapEnabled = isProduction 6 | ? config.build.productionSourceMap 7 | : config.dev.cssSourceMap 8 | 9 | module.exports = { 10 | loaders: utils.cssLoaders({ 11 | sourceMap: sourceMapEnabled, 12 | extract: isProduction 13 | }), 14 | cssSourceMap: sourceMapEnabled, 15 | cacheBusting: config.dev.cacheBusting, 16 | transformToRequire: { 17 | video: ['src', 'poster'], 18 | source: 'src', 19 | img: 'src', 20 | image: 'xlink:href' 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /pkg/h5/build/webpack.test.conf.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | // This is the webpack config used for unit tests. 3 | 4 | const utils = require('./utils') 5 | const webpack = require('webpack') 6 | const merge = require('webpack-merge') 7 | const baseWebpackConfig = require('./webpack.base.conf') 8 | 9 | const webpackConfig = merge(baseWebpackConfig, { 10 | // use inline sourcemap for karma-sourcemap-loader 11 | module: { 12 | rules: utils.styleLoaders() 13 | }, 14 | devtool: '#inline-source-map', 15 | resolveLoader: { 16 | alias: { 17 | // necessary to to make lang="scss" work in test when using vue-loader's ?inject option 18 | // see discussion at https://github.com/vuejs/vue-loader/issues/724 19 | 'scss-loader': 'sass-loader' 20 | } 21 | }, 22 | plugins: [ 23 | new webpack.DefinePlugin({ 24 | 'process.env': require('../config/test.env') 25 | }) 26 | ] 27 | }) 28 | 29 | // no need for app entry during tests 30 | delete webpackConfig.entry 31 | 32 | module.exports = webpackConfig 33 | -------------------------------------------------------------------------------- /pkg/h5/config/dev.env.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const merge = require('webpack-merge') 3 | const prodEnv = require('./prod.env') 4 | 5 | module.exports = merge(prodEnv, { 6 | NODE_ENV: '"development"', 7 | BASE_URL: '"//127.0.0.1:8080/"' 8 | }) 9 | -------------------------------------------------------------------------------- /pkg/h5/config/prod.env.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | module.exports = { 3 | NODE_ENV: '"production"', 4 | BASE_URL: '"//lime.bullteam.cn/"' 5 | } 6 | -------------------------------------------------------------------------------- /pkg/h5/config/test.env.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const merge = require('webpack-merge') 3 | const devEnv = require('./dev.env') 4 | 5 | module.exports = merge(devEnv, { 6 | NODE_ENV: '"testing"' 7 | }) 8 | -------------------------------------------------------------------------------- /pkg/h5/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Lime 8 | 9 | 10 |
11 | 12 | 13 | -------------------------------------------------------------------------------- /pkg/h5/src/App.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 12 | 13 | 21 | -------------------------------------------------------------------------------- /pkg/h5/src/assets/lock.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pkg/h5/src/assets/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /pkg/h5/src/assets/user.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pkg/h5/src/components/common/HorizontalList.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 25 | 26 | 29 | -------------------------------------------------------------------------------- /pkg/h5/src/components/common/Similar.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 36 | 37 | 40 | -------------------------------------------------------------------------------- /pkg/h5/src/components/common/TopMenu.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 19 | 20 | 50 | -------------------------------------------------------------------------------- /pkg/h5/src/components/common/VerticalList.vue: -------------------------------------------------------------------------------- 1 | 31 | 32 | 38 | 39 | 42 | -------------------------------------------------------------------------------- /pkg/h5/src/components/rate/star_half.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/limeteam/lime/7708031446a878b090487588b8f4bcca63514282/pkg/h5/src/components/rate/star_half.png -------------------------------------------------------------------------------- /pkg/h5/src/components/rate/star_off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/limeteam/lime/7708031446a878b090487588b8f4bcca63514282/pkg/h5/src/components/rate/star_off.png -------------------------------------------------------------------------------- /pkg/h5/src/components/rate/star_on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/limeteam/lime/7708031446a878b090487588b8f4bcca63514282/pkg/h5/src/components/rate/star_on.png -------------------------------------------------------------------------------- /pkg/h5/src/main.js: -------------------------------------------------------------------------------- 1 | // The Vue build version to load with the `import` command 2 | // (runtime-only or standalone) has been set in webpack.base.conf with an alias. 3 | import Vue from 'vue' 4 | import Mint from 'mint-ui'; 5 | import 'mint-ui/lib/style.css' 6 | import router from './router' 7 | import store from './store' 8 | import './permission' // permission control 9 | import App from './App' 10 | 11 | 12 | Vue.use(Mint); 13 | 14 | import 'animate.css'; 15 | import '@/assets/less/reset.css'; 16 | 17 | Vue.config.productionTip = false 18 | 19 | /* eslint-disable no-new */ 20 | new Vue({ 21 | el: '#app', 22 | router, 23 | store, 24 | components: {App}, 25 | template: '' 26 | }) 27 | -------------------------------------------------------------------------------- /pkg/h5/src/permission.js: -------------------------------------------------------------------------------- 1 | import router from './router' 2 | import store from './store' 3 | import { Toast } from 'mint-ui' 4 | import NProgress from 'nprogress' // progress bar 5 | import 'nprogress/nprogress.css' // progress bar style 6 | import { getToken } from '@/utils/auth' // get token from cookie 7 | 8 | NProgress.configure({ showSpinner: false }) // NProgress Configuration 9 | 10 | const whiteList = ['/login', '/register', '/home'] // no redirect whitelist 11 | 12 | router.beforeEach(async (to, from, next) => { 13 | // start progress bar 14 | NProgress.start() 15 | const hasToken = getToken() 16 | 17 | if (hasToken) { 18 | if (to.path === '/login') { 19 | next({ path: '/' }) 20 | NProgress.done() 21 | } else { 22 | next() 23 | NProgress.done() 24 | } 25 | } else { 26 | if (whiteList.indexOf(to.path) !== -1) { 27 | next() 28 | } else { 29 | await store.dispatch('resetToken') 30 | Toast({ 31 | message: '重新登陆', 32 | position: 'bottom', 33 | duration: 5000 34 | }); 35 | next(`/login`) 36 | NProgress.done() 37 | } 38 | } 39 | }) 40 | 41 | router.afterEach(() => { 42 | // finish progress bar 43 | NProgress.done() 44 | }) 45 | -------------------------------------------------------------------------------- /pkg/h5/src/store/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Vuex from 'vuex' 3 | 4 | import state from './modules/state' 5 | import actions from './modules/actions' 6 | import mutations from './modules/mutations' 7 | 8 | Vue.use(Vuex); 9 | 10 | export default new Vuex.Store({ 11 | state, 12 | actions, 13 | mutations 14 | }) 15 | -------------------------------------------------------------------------------- /pkg/h5/src/store/modules/state.js: -------------------------------------------------------------------------------- 1 | import { getToken} from '@/utils/auth' 2 | const state = { 3 | userInfo: null, 4 | types: [ 5 | { 6 | type: 1, 7 | title: '玄幻' 8 | }, 9 | { 10 | type: 2, 11 | title: '修真' 12 | }, 13 | { 14 | type: 3, 15 | title: '都市' 16 | }, 17 | { 18 | type: 4, 19 | title: '历史' 20 | }, 21 | { 22 | type: 5, 23 | title: '游戏' 24 | } 25 | ], 26 | cover: false, 27 | menu: false, 28 | currentCpt: 1, 29 | night: false, 30 | nightText: '夜间', 31 | font: false, 32 | fontSize: 16, 33 | currentStyle: 'style1', 34 | styles: ['style1', 'style2', 'style3', 'style4', 'style5'], 35 | token: getToken(), 36 | username: '', 37 | faceicon: '' 38 | } 39 | export default state; 40 | -------------------------------------------------------------------------------- /pkg/h5/src/store/modules/types.js: -------------------------------------------------------------------------------- 1 | // 类型名称 2 | export const TYPE = 'TYPE'; 3 | // 显示章节目录 4 | export const TITLE_COVER = 'TITLE_COVER'; 5 | // 显示上下导航 6 | export const MENU = 'MENU'; 7 | // 当前章节 8 | export const CURRENT_CPT = 'CURRENT_CPT'; 9 | // 上一章 10 | export const PRE_CPT = 'PRE_CPT'; 11 | // 下一章 12 | export const NEXT_CPT = 'NEXT_CPT'; 13 | // 日夜模式 14 | export const SWITCH_STYLE = 'SWITCH_STYLE'; 15 | // 字体显示 16 | export const FONT = 'FONT'; 17 | // 字体变大 18 | export const BIG_SIZE = 'BIG_SIZE'; 19 | // 字体变小 20 | export const SMALL_SIZE = 'SMALL_SIZE'; 21 | // 日间模式 22 | export const DAY_STYLE = 'DAY_STYLE'; 23 | // 个人信息 24 | export const PERSON_INFO = 'PERSON_INFO'; 25 | -------------------------------------------------------------------------------- /pkg/h5/src/utils/auth.js: -------------------------------------------------------------------------------- 1 | import Cookies from 'js-cookie' 2 | 3 | const TokenKey = 'Lime-Token' 4 | 5 | export function getToken() { 6 | return Cookies.get(TokenKey) 7 | } 8 | 9 | export function setToken(token) { 10 | return Cookies.set(TokenKey, token) 11 | } 12 | 13 | export function removeToken() { 14 | return Cookies.remove(TokenKey) 15 | } 16 | -------------------------------------------------------------------------------- /pkg/h5/static/images/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/limeteam/lime/7708031446a878b090487588b8f4bcca63514282/pkg/h5/static/images/1.jpg -------------------------------------------------------------------------------- /pkg/h5/static/images/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/limeteam/lime/7708031446a878b090487588b8f4bcca63514282/pkg/h5/static/images/2.jpg -------------------------------------------------------------------------------- /pkg/h5/static/images/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/limeteam/lime/7708031446a878b090487588b8f4bcca63514282/pkg/h5/static/images/3.jpg -------------------------------------------------------------------------------- /pkg/h5/static/images/4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/limeteam/lime/7708031446a878b090487588b8f4bcca63514282/pkg/h5/static/images/4.jpg -------------------------------------------------------------------------------- /pkg/h5/static/images/5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/limeteam/lime/7708031446a878b090487588b8f4bcca63514282/pkg/h5/static/images/5.jpg -------------------------------------------------------------------------------- /pkg/h5/static/images/sprite.0.50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/limeteam/lime/7708031446a878b090487588b8f4bcca63514282/pkg/h5/static/images/sprite.0.50.png -------------------------------------------------------------------------------- /pkg/h5/static/images/sprite@2x.0.50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/limeteam/lime/7708031446a878b090487588b8f4bcca63514282/pkg/h5/static/images/sprite@2x.0.50.png -------------------------------------------------------------------------------- /pkg/ui/.editorconfig: -------------------------------------------------------------------------------- 1 | # https://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | end_of_line = lf 9 | insert_final_newline = true 10 | trim_trailing_whitespace = true 11 | 12 | [*.md] 13 | insert_final_newline = false 14 | trim_trailing_whitespace = false 15 | -------------------------------------------------------------------------------- /pkg/ui/.env.development: -------------------------------------------------------------------------------- 1 | # just a flag 2 | ENV = 'development' 3 | 4 | # base api 5 | #VUE_APP_BASE_API = '/dev-api' 6 | VUE_CLI_BABEL_TRANSPILE_MODULES = true 7 | 8 | VUE_APP_BASE_API = '//api.auth.bullteam.cn' 9 | #VUE_APP_BASE_API = '//127.0.0.1:8082' 10 | VUE_APP_CONFIG_API = '//127.0.0.1:8080' 11 | -------------------------------------------------------------------------------- /pkg/ui/.env.production: -------------------------------------------------------------------------------- 1 | # just a flag 2 | ENV = 'production' 3 | 4 | # base api 5 | VUE_APP_BASE_API = '//api.auth.bullteam.cn' 6 | 7 | VUE_APP_CONFIG_API = '//lime.bullteam.cn/' 8 | -------------------------------------------------------------------------------- /pkg/ui/.env.staging: -------------------------------------------------------------------------------- 1 | NODE_ENV = production 2 | 3 | # just a flag 4 | ENV = 'staging' 5 | 6 | # base api 7 | VUE_APP_BASE_API = '/stage-api' 8 | 9 | -------------------------------------------------------------------------------- /pkg/ui/.eslintignore: -------------------------------------------------------------------------------- 1 | build/*.js 2 | src/assets 3 | public 4 | dist 5 | -------------------------------------------------------------------------------- /pkg/ui/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | dist/ 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | **/*.log 8 | 9 | tests/**/coverage/ 10 | tests/e2e/reports 11 | selenium-debug.log 12 | 13 | # Editor directories and files 14 | .idea 15 | .vscode 16 | *.suo 17 | *.ntvs* 18 | *.njsproj 19 | *.sln 20 | *.local 21 | 22 | package-lock.json 23 | yarn.lock 24 | -------------------------------------------------------------------------------- /pkg/ui/.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 10 3 | script: npm run test 4 | notifications: 5 | email: false 6 | -------------------------------------------------------------------------------- /pkg/ui/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017-present PanJiaChen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /pkg/ui/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/app' 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /pkg/ui/build/index.js: -------------------------------------------------------------------------------- 1 | const { run } = require('runjs') 2 | const chalk = require('chalk') 3 | const config = require('../vue.config.js') 4 | const rawArgv = process.argv.slice(2) 5 | const args = rawArgv.join(' ') 6 | 7 | if (process.env.npm_config_preview || rawArgv.includes('--preview')) { 8 | const report = rawArgv.includes('--report') 9 | 10 | run(`vue-cli-service build ${args}`) 11 | 12 | const port = 9526 13 | const publicPath = config.publicPath 14 | 15 | var connect = require('connect') 16 | var serveStatic = require('serve-static') 17 | const app = connect() 18 | 19 | app.use( 20 | publicPath, 21 | serveStatic('./dist', { 22 | index: ['index.html', '/'] 23 | }) 24 | ) 25 | 26 | app.listen(port, function () { 27 | console.log(chalk.green(`> Preview at http://localhost:${port}${publicPath}`)) 28 | if (report) { 29 | console.log(chalk.green(`> Report at http://localhost:${port}${publicPath}report.html`)) 30 | } 31 | 32 | }) 33 | } else { 34 | run(`vue-cli-service build ${args}`) 35 | } 36 | -------------------------------------------------------------------------------- /pkg/ui/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | moduleFileExtensions: ['js', 'jsx', 'json', 'vue'], 3 | transform: { 4 | '^.+\\.vue$': 'vue-jest', 5 | '.+\\.(css|styl|less|sass|scss|svg|png|jpg|ttf|woff|woff2)$': 6 | 'jest-transform-stub', 7 | '^.+\\.jsx?$': 'babel-jest' 8 | }, 9 | moduleNameMapper: { 10 | '^@/(.*)$': '/src/$1' 11 | }, 12 | snapshotSerializers: ['jest-serializer-vue'], 13 | testMatch: [ 14 | '**/tests/unit/**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)' 15 | ], 16 | collectCoverageFrom: ['src/utils/**/*.{js,vue}', '!src/utils/auth.js', '!src/utils/request.js', 'src/components/**/*.{js,vue}'], 17 | coverageDirectory: '/tests/unit/coverage', 18 | // 'collectCoverage': true, 19 | 'coverageReporters': [ 20 | 'lcov', 21 | 'text-summary' 22 | ], 23 | testURL: 'http://localhost/' 24 | } 25 | -------------------------------------------------------------------------------- /pkg/ui/jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": "./", 4 | "paths": { 5 | "@/*": ["src/*"] 6 | } 7 | }, 8 | "exclude": ["node_modules", "dist"] 9 | } -------------------------------------------------------------------------------- /pkg/ui/mock/index.js: -------------------------------------------------------------------------------- 1 | import Mock from 'mockjs' 2 | import { param2Obj } from '../src/utils' 3 | 4 | 5 | const mocks = [ 6 | ] 7 | 8 | // for v1 mock 9 | // please use it cautiously, it will redefine XMLHttpRequest, 10 | // which will cause many of your third-party libraries to be invalidated(like progress event). 11 | export function mockXHR() { 12 | // mock patch 13 | // https://github.com/nuysoft/Mock/issues/300 14 | Mock.XHR.prototype.proxy_send = Mock.XHR.prototype.send 15 | Mock.XHR.prototype.send = function() { 16 | if (this.custom.xhr) { 17 | this.custom.xhr.withCredentials = this.withCredentials || false 18 | 19 | if (this.responseType) { 20 | this.custom.xhr.responseType = this.responseType 21 | } 22 | } 23 | this.proxy_send(...arguments) 24 | } 25 | 26 | function XHR2ExpressReqWrap(respond) { 27 | return function(options) { 28 | let result = null 29 | if (respond instanceof Function) { 30 | const { body, type, url } = options 31 | // https://expressjs.com/en/4x/api.html#req 32 | result = respond({ 33 | method: type, 34 | body: JSON.parse(body), 35 | query: param2Obj(url) 36 | }) 37 | } else { 38 | result = respond 39 | } 40 | return Mock.mock(result) 41 | } 42 | } 43 | 44 | for (const i of mocks) { 45 | Mock.mock(new RegExp(i.url), i.type || 'get', XHR2ExpressReqWrap(i.response)) 46 | } 47 | } 48 | 49 | export default mocks 50 | -------------------------------------------------------------------------------- /pkg/ui/plop-templates/component/index.hbs: -------------------------------------------------------------------------------- 1 | {{#if template}} 2 | 5 | {{/if}} 6 | 7 | {{#if script}} 8 | 20 | {{/if}} 21 | 22 | {{#if style}} 23 | 26 | {{/if}} 27 | -------------------------------------------------------------------------------- /pkg/ui/plop-templates/component/prompt.js: -------------------------------------------------------------------------------- 1 | const { notEmpty } = require('../utils.js') 2 | 3 | module.exports = { 4 | description: 'generate vue component', 5 | prompts: [{ 6 | type: 'input', 7 | name: 'name', 8 | message: 'component name please', 9 | validate: notEmpty('name') 10 | }, 11 | { 12 | type: 'checkbox', 13 | name: 'blocks', 14 | message: 'Blocks:', 15 | choices: [{ 16 | name: '