├── pkg ├── ui │ ├── src │ │ ├── views │ │ │ ├── setting │ │ │ │ └── wechatMenu.vue │ │ │ ├── category │ │ │ │ └── emun │ │ │ │ │ └── index.js │ │ │ ├── comicCategory │ │ │ │ └── emun │ │ │ │ │ └── index.js │ │ │ ├── redirect │ │ │ │ └── index.vue │ │ │ ├── users │ │ │ │ └── emun │ │ │ │ │ └── index.js │ │ │ ├── login │ │ │ │ └── auth-redirect.vue │ │ │ ├── permission │ │ │ │ ├── page.vue │ │ │ │ └── components │ │ │ │ │ └── SwitchRoles.vue │ │ │ ├── dashboard │ │ │ │ └── index.vue │ │ │ ├── comics │ │ │ │ └── emun │ │ │ │ │ └── index.js │ │ │ ├── books │ │ │ │ └── emun │ │ │ │ │ └── index.js │ │ │ └── guide │ │ │ │ ├── index.vue │ │ │ │ └── steps.js │ │ ├── assets │ │ │ ├── logo-min.png │ │ │ ├── 401_images │ │ │ │ └── 401.gif │ │ │ ├── 404_images │ │ │ │ ├── 404.png │ │ │ │ └── 404_cloud.png │ │ │ └── custom-theme │ │ │ │ └── fonts │ │ │ │ ├── element-icons.ttf │ │ │ │ └── element-icons.woff │ │ ├── App.vue │ │ ├── icons │ │ │ ├── svg │ │ │ │ ├── chart.svg │ │ │ │ ├── size.svg │ │ │ │ ├── link.svg │ │ │ │ ├── component.svg │ │ │ │ ├── guide.svg │ │ │ │ ├── money.svg │ │ │ │ ├── email.svg │ │ │ │ ├── drag.svg │ │ │ │ ├── documentation.svg │ │ │ │ ├── fullscreen.svg │ │ │ │ ├── lock.svg │ │ │ │ ├── user.svg │ │ │ │ ├── excel.svg │ │ │ │ ├── example.svg │ │ │ │ ├── star.svg │ │ │ │ ├── table.svg │ │ │ │ ├── search.svg │ │ │ │ ├── password.svg │ │ │ │ ├── education.svg │ │ │ │ ├── tab.svg │ │ │ │ ├── message.svg │ │ │ │ ├── theme.svg │ │ │ │ ├── peoples.svg │ │ │ │ ├── edit.svg │ │ │ │ ├── nested.svg │ │ │ │ ├── tree-table.svg │ │ │ │ ├── eye.svg │ │ │ │ ├── clipboard.svg │ │ │ │ ├── list.svg │ │ │ │ ├── icon.svg │ │ │ │ ├── international.svg │ │ │ │ ├── wechat.svg │ │ │ │ ├── skill.svg │ │ │ │ ├── people.svg │ │ │ │ ├── language.svg │ │ │ │ ├── eye-open.svg │ │ │ │ ├── 404.svg │ │ │ │ └── zip.svg │ │ │ ├── index.js │ │ │ └── svgo.yml │ │ ├── api │ │ │ ├── lime-admin │ │ │ │ ├── upload.js │ │ │ │ ├── enum │ │ │ │ │ └── index.js │ │ │ │ ├── comment.js │ │ │ │ ├── comicComment.js │ │ │ │ ├── setting.js │ │ │ │ ├── category.js │ │ │ │ ├── comicCategory.js │ │ │ │ ├── chapter.js │ │ │ │ ├── comicChapter.js │ │ │ │ ├── users.js │ │ │ │ ├── distributorLevel.js │ │ │ │ └── distributor.js │ │ │ ├── auth-admin │ │ │ │ ├── enum │ │ │ │ │ └── index.js │ │ │ │ ├── auth.js │ │ │ │ └── user.js │ │ │ └── user.js │ │ ├── components │ │ │ ├── ImageCropper │ │ │ │ └── utils │ │ │ │ │ ├── mimes.js │ │ │ │ │ ├── data2blob.js │ │ │ │ │ └── effectRipple.js │ │ │ ├── Tinymce │ │ │ │ ├── toolbar.js │ │ │ │ └── plugins.js │ │ │ ├── MarkdownEditor │ │ │ │ └── default-options.js │ │ │ ├── Screenfull │ │ │ │ └── index.vue │ │ │ └── Hamburger │ │ │ │ └── index.vue │ │ ├── utils │ │ │ ├── get-page-title.js │ │ │ ├── auth.js │ │ │ ├── permission.js │ │ │ ├── clipboard.js │ │ │ ├── error-log.js │ │ │ └── open-window.js │ │ ├── layout │ │ │ ├── components │ │ │ │ ├── index.js │ │ │ │ ├── Sidebar │ │ │ │ │ ├── Item.vue │ │ │ │ │ ├── FixiOSBug.js │ │ │ │ │ └── Link.vue │ │ │ │ └── AppMain.vue │ │ │ └── mixin │ │ │ │ └── ResizeHandler.js │ │ ├── directive │ │ │ ├── waves │ │ │ │ ├── index.js │ │ │ │ └── waves.css │ │ │ ├── el-drag-dialog │ │ │ │ └── index.js │ │ │ ├── clipboard │ │ │ │ └── index.js │ │ │ ├── permission │ │ │ │ ├── index.js │ │ │ │ └── permission.js │ │ │ └── el-table │ │ │ │ ├── index.js │ │ │ │ └── adaptive.js │ │ ├── store │ │ │ ├── modules │ │ │ │ ├── errorLog.js │ │ │ │ └── settings.js │ │ │ ├── getters.js │ │ │ └── index.js │ │ ├── vendor │ │ │ └── Export2Zip.js │ │ ├── styles │ │ │ ├── variables.scss │ │ │ ├── element-variables.scss │ │ │ ├── transition.scss │ │ │ └── element-ui.scss │ │ └── settings.js │ ├── .eslintignore │ ├── babel.config.js │ ├── postcss.config.js │ ├── public │ │ ├── favicon.ico │ │ └── index.html │ ├── tests │ │ └── unit │ │ │ ├── .eslintrc.js │ │ │ ├── components │ │ │ ├── Hamburger.spec.js │ │ │ └── SvgIcon.spec.js │ │ │ └── utils │ │ │ ├── formatTime.spec.js │ │ │ ├── validate.spec.js │ │ │ └── parseTime.spec.js │ ├── .travis.yml │ ├── .env.staging │ ├── .env.production │ ├── jsconfig.json │ ├── plop-templates │ │ ├── utils.js │ │ ├── store │ │ │ └── index.hbs │ │ ├── view │ │ │ ├── index.hbs │ │ │ └── prompt.js │ │ └── component │ │ │ ├── index.hbs │ │ │ └── prompt.js │ ├── .env.development │ ├── .editorconfig │ ├── .gitignore │ ├── plopfile.js │ ├── jest.config.js │ ├── build │ │ └── index.js │ ├── LICENSE │ └── mock │ │ └── index.js ├── h5 │ ├── .eslintignore │ ├── static │ │ └── images │ │ │ ├── 1.jpg │ │ │ ├── 2.jpg │ │ │ ├── 3.jpg │ │ │ ├── 4.jpg │ │ │ ├── 5.jpg │ │ │ ├── sprite.0.50.png │ │ │ └── sprite@2x.0.50.png │ ├── src │ │ ├── components │ │ │ ├── rate │ │ │ │ ├── star_off.png │ │ │ │ ├── star_on.png │ │ │ │ └── star_half.png │ │ │ └── common │ │ │ │ ├── HorizontalList.vue │ │ │ │ ├── Similar.vue │ │ │ │ ├── TopMenu.vue │ │ │ │ └── VerticalList.vue │ │ ├── store │ │ │ ├── index.js │ │ │ └── modules │ │ │ │ ├── types.js │ │ │ │ └── state.js │ │ ├── assets │ │ │ ├── logo.svg │ │ │ ├── lock.svg │ │ │ └── user.svg │ │ ├── utils │ │ │ └── auth.js │ │ ├── App.vue │ │ ├── main.js │ │ └── permission.js │ ├── README.md │ ├── config │ │ ├── prod.env.js │ │ ├── test.env.js │ │ └── dev.env.js │ ├── .editorconfig │ ├── .gitignore │ ├── .postcssrc.js │ ├── index.html │ ├── .babelrc │ ├── build │ │ ├── vue-loader.conf.js │ │ ├── webpack.test.conf.js │ │ ├── build.js │ │ └── check-versions.js │ └── .eslintrc.js ├── api │ ├── admin │ │ ├── model │ │ │ ├── base.go │ │ │ ├── spider.go │ │ │ ├── chapter.go │ │ │ ├── book.go │ │ │ ├── comicCategory.go │ │ │ ├── category.go │ │ │ ├── comicComments.go │ │ │ ├── comments.go │ │ │ ├── config.go │ │ │ ├── distributor.go │ │ │ ├── comicChapters.go │ │ │ ├── chapters.go │ │ │ └── distributorLevel.go │ │ ├── dto │ │ │ ├── upload.go │ │ │ ├── chapter.go │ │ │ ├── general.go │ │ │ ├── init.go │ │ │ ├── comicComment.go │ │ │ ├── comment.go │ │ │ └── comicCategory.go │ │ ├── controllers │ │ │ ├── rule.go │ │ │ ├── upload.go │ │ │ ├── setting.go │ │ │ ├── base.go │ │ │ ├── comments.go │ │ │ └── comicComments.go │ │ ├── service │ │ │ ├── upload.go │ │ │ ├── comments.go │ │ │ └── comicComments.go │ │ ├── domain │ │ │ └── task │ │ │ │ └── task_lock.go │ │ └── dao │ │ │ ├── spider.go │ │ │ ├── book.go │ │ │ ├── chapter.go │ │ │ ├── comment.go │ │ │ ├── comicComment.go │ │ │ ├── category.go │ │ │ └── comicCategory.go │ ├── front │ │ ├── domain │ │ │ ├── wechat │ │ │ │ └── subscribe.go │ │ │ └── auth │ │ │ │ ├── login │ │ │ │ └── general.go │ │ │ │ └── account.go │ │ ├── dao │ │ │ ├── category.go │ │ │ ├── config.go │ │ │ ├── chapters.go │ │ │ └── books.go │ │ ├── controllers │ │ │ ├── wechat.go │ │ │ ├── chapters.go │ │ │ └── books.go │ │ └── dto │ │ │ ├── general.go │ │ │ ├── user.go │ │ │ └── init.go │ ├── middleware │ │ ├── auth.go │ │ ├── cors.go │ │ └── file.go │ ├── router │ │ ├── router.go │ │ └── front.go │ └── utils │ │ └── e │ │ └── code.go ├── crawler │ ├── novels │ │ ├── biquge │ │ │ ├── list.go │ │ │ ├── book.go │ │ │ └── chapter.go │ │ ├── store.go │ │ └── aoyuge │ │ │ └── file.go │ └── core │ │ └── task.go ├── down │ ├── site │ │ ├── jjxs.go │ │ └── site_chromedp.go │ ├── convert.go │ ├── job.go │ ├── util.go │ ├── store │ │ └── store.go │ ├── output │ │ ├── txt.go │ │ └── output.go │ ├── search.go │ └── syncstore.go └── common │ ├── types.go │ ├── db │ ├── mongo_test.go │ └── db_test.go │ └── cache │ └── init.go ├── .gitignore ├── docs └── image │ ├── 1.png │ ├── 2.png │ ├── 3.png │ ├── 4.png │ ├── wx.jpg │ ├── logo.png │ ├── books.png │ ├── upload.png │ ├── category.png │ └── chapters.png ├── CHANGELOG.md ├── scripts ├── build.sh ├── apppkg.sh ├── run.sh ├── tag.sh └── version.sh ├── main.go ├── cmd └── cobra.go ├── config └── in-cluster.yaml └── Makefile /pkg/ui/src/views/setting/wechatMenu.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pkg/ui/.eslintignore: -------------------------------------------------------------------------------- 1 | build/*.js 2 | src/assets 3 | public 4 | dist 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | lime 2 | .idea/ 3 | data/ 4 | go.sum 5 | config/in-local.yaml 6 | -------------------------------------------------------------------------------- /docs/image/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/limeteam/lime/HEAD/docs/image/1.png -------------------------------------------------------------------------------- /docs/image/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/limeteam/lime/HEAD/docs/image/2.png -------------------------------------------------------------------------------- /docs/image/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/limeteam/lime/HEAD/docs/image/3.png -------------------------------------------------------------------------------- /docs/image/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/limeteam/lime/HEAD/docs/image/4.png -------------------------------------------------------------------------------- /docs/image/wx.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/limeteam/lime/HEAD/docs/image/wx.jpg -------------------------------------------------------------------------------- /docs/image/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/limeteam/lime/HEAD/docs/image/logo.png -------------------------------------------------------------------------------- /docs/image/books.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/limeteam/lime/HEAD/docs/image/books.png -------------------------------------------------------------------------------- /docs/image/upload.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/limeteam/lime/HEAD/docs/image/upload.png -------------------------------------------------------------------------------- /pkg/h5/.eslintignore: -------------------------------------------------------------------------------- 1 | /build/ 2 | /config/ 3 | /dist/ 4 | /*.js 5 | /test/unit/coverage/ 6 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | v0.0.1 init 初始化 4 | V0.1 小说内容管理: 分类管理、小说列表、上传小说、热词列表、评论列表 5 | -------------------------------------------------------------------------------- /docs/image/category.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/limeteam/lime/HEAD/docs/image/category.png -------------------------------------------------------------------------------- /docs/image/chapters.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/limeteam/lime/HEAD/docs/image/chapters.png -------------------------------------------------------------------------------- /pkg/ui/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/app' 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /pkg/h5/static/images/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/limeteam/lime/HEAD/pkg/h5/static/images/1.jpg -------------------------------------------------------------------------------- /pkg/h5/static/images/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/limeteam/lime/HEAD/pkg/h5/static/images/2.jpg -------------------------------------------------------------------------------- /pkg/h5/static/images/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/limeteam/lime/HEAD/pkg/h5/static/images/3.jpg -------------------------------------------------------------------------------- /pkg/h5/static/images/4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/limeteam/lime/HEAD/pkg/h5/static/images/4.jpg -------------------------------------------------------------------------------- /pkg/h5/static/images/5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/limeteam/lime/HEAD/pkg/h5/static/images/5.jpg -------------------------------------------------------------------------------- /pkg/ui/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | autoprefixer: {} 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /pkg/ui/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/limeteam/lime/HEAD/pkg/ui/public/favicon.ico -------------------------------------------------------------------------------- /pkg/ui/tests/unit/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | jest: true 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /pkg/ui/src/assets/logo-min.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/limeteam/lime/HEAD/pkg/ui/src/assets/logo-min.png -------------------------------------------------------------------------------- /pkg/api/admin/model/base.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | type Pagination struct { 4 | Start int 5 | Limit int 6 | } 7 | -------------------------------------------------------------------------------- /scripts/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | export GO111MODULE=on 4 | export GOPROXY=https://goproxy.io 5 | go build -o lime -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "lime/cmd" 5 | ) 6 | 7 | func main() { 8 | cmd.Execute() 9 | } 10 | -------------------------------------------------------------------------------- /pkg/h5/static/images/sprite.0.50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/limeteam/lime/HEAD/pkg/h5/static/images/sprite.0.50.png -------------------------------------------------------------------------------- /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/src/assets/401_images/401.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/limeteam/lime/HEAD/pkg/ui/src/assets/401_images/401.gif -------------------------------------------------------------------------------- /pkg/ui/src/assets/404_images/404.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/limeteam/lime/HEAD/pkg/ui/src/assets/404_images/404.png -------------------------------------------------------------------------------- /pkg/h5/src/components/rate/star_off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/limeteam/lime/HEAD/pkg/h5/src/components/rate/star_off.png -------------------------------------------------------------------------------- /pkg/h5/src/components/rate/star_on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/limeteam/lime/HEAD/pkg/h5/src/components/rate/star_on.png -------------------------------------------------------------------------------- /pkg/h5/static/images/sprite@2x.0.50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/limeteam/lime/HEAD/pkg/h5/static/images/sprite@2x.0.50.png -------------------------------------------------------------------------------- /pkg/h5/README.md: -------------------------------------------------------------------------------- 1 | H5 阅读 2 | 3 | 4 | 特别鸣谢: 5 | 6 | 本H5项目查考: [https://github.com/YepFury/reader](https://github.com/YepFury/reader) -------------------------------------------------------------------------------- /pkg/h5/src/components/rate/star_half.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/limeteam/lime/HEAD/pkg/h5/src/components/rate/star_half.png -------------------------------------------------------------------------------- /pkg/ui/src/assets/404_images/404_cloud.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/limeteam/lime/HEAD/pkg/ui/src/assets/404_images/404_cloud.png -------------------------------------------------------------------------------- /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/h5/config/prod.env.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | module.exports = { 3 | NODE_ENV: '"production"', 4 | BASE_URL: '"//lime.bullteam.cn/"' 5 | } 6 | -------------------------------------------------------------------------------- /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/src/assets/custom-theme/fonts/element-icons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/limeteam/lime/HEAD/pkg/ui/src/assets/custom-theme/fonts/element-icons.ttf -------------------------------------------------------------------------------- /pkg/ui/src/assets/custom-theme/fonts/element-icons.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/limeteam/lime/HEAD/pkg/ui/src/assets/custom-theme/fonts/element-icons.woff -------------------------------------------------------------------------------- /scripts/apppkg.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | # get import path of current go project. 4 | ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" 5 | echo ${ROOT//$GOPATH\/src\//} 6 | -------------------------------------------------------------------------------- /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/jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": "./", 4 | "paths": { 5 | "@/*": ["src/*"] 6 | } 7 | }, 8 | "exclude": ["node_modules", "dist"] 9 | } -------------------------------------------------------------------------------- /pkg/ui/src/App.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 12 | -------------------------------------------------------------------------------- /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/ui/src/views/category/emun/index.js: -------------------------------------------------------------------------------- 1 | export const CATEGORY_CHANNEL = [ 2 | { key: 0, display_name: '全部' }, 3 | { key: 1, display_name: '男生' }, 4 | { key: 2, display_name: '女生' } 5 | ] -------------------------------------------------------------------------------- /pkg/ui/src/views/comicCategory/emun/index.js: -------------------------------------------------------------------------------- 1 | export const CATEGORY_CHANNEL = [ 2 | { key: 0, display_name: '全部' }, 3 | { key: 1, display_name: '男生' }, 4 | { key: 2, display_name: '女生' } 5 | ] -------------------------------------------------------------------------------- /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/ui/src/icons/svg/chart.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pkg/ui/src/api/lime-admin/upload.js: -------------------------------------------------------------------------------- 1 | import request from './request' 2 | 3 | export function getQiniuToken() { 4 | return request({ 5 | url: '/admin/upload/qiniuToken', 6 | method: 'GET' 7 | }) 8 | } 9 | -------------------------------------------------------------------------------- /pkg/ui/src/components/ImageCropper/utils/mimes.js: -------------------------------------------------------------------------------- 1 | export default { 2 | 'jpg': 'image/jpeg', 3 | 'png': 'image/png', 4 | 'gif': 'image/gif', 5 | 'svg': 'image/svg+xml', 6 | 'psd': 'image/photoshop' 7 | } 8 | -------------------------------------------------------------------------------- /pkg/ui/plop-templates/utils.js: -------------------------------------------------------------------------------- 1 | exports.notEmpty = name => { 2 | return v => { 3 | if (!v || v.trim === '') { 4 | return `${name} is required` 5 | } else { 6 | return true 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /scripts/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | export MYSQL_PASSWORD=123456 4 | export MYSQL_HOST=127.0.0.1 5 | export MYSQL_DB=lime 6 | export MYSQL_PORT=3306 7 | export MYSQL_USERNAME=root 8 | 9 | ./lime server ./config/in-local.yaml -p 808 -------------------------------------------------------------------------------- /pkg/ui/src/icons/svg/size.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /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/ui/src/api/auth-admin/enum/index.js: -------------------------------------------------------------------------------- 1 | export const TOKEN_EXPIRATION = [ 2 | // 需要登录 3 | 111001002, 4 | // token 无效 5 | 111001003, 6 | // token 过期 7 | 111001004, 8 | // token 无法解密 9 | 109001010 10 | ] 11 | export const NOT_SHOW_MSG = [] 12 | -------------------------------------------------------------------------------- /pkg/ui/src/api/lime-admin/enum/index.js: -------------------------------------------------------------------------------- 1 | export const TOKEN_EXPIRATION = [ 2 | // 需要登录 3 | 111001002, 4 | // token 无效 5 | 111001003, 6 | // token 过期 7 | 111001004, 8 | // token 无法解密 9 | 109001010 10 | ] 11 | export const NOT_SHOW_MSG = [] 12 | -------------------------------------------------------------------------------- /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/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/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/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/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/ui/plop-templates/store/index.hbs: -------------------------------------------------------------------------------- 1 | {{#if state}} 2 | const state = {} 3 | {{/if}} 4 | 5 | {{#if mutations}} 6 | const mutations = {} 7 | {{/if}} 8 | 9 | {{#if actions}} 10 | const actions = {} 11 | {{/if}} 12 | 13 | export default { 14 | namespaced: true, 15 | {{options}} 16 | } 17 | -------------------------------------------------------------------------------- /pkg/ui/src/utils/get-page-title.js: -------------------------------------------------------------------------------- 1 | import defaultSettings from '@/settings' 2 | 3 | const title = defaultSettings.title || 'Zeus Admin' 4 | 5 | export default function getPageTitle(pageTitle) { 6 | if (pageTitle) { 7 | return `${pageTitle} - ${title}` 8 | } 9 | return `${title}` 10 | } 11 | -------------------------------------------------------------------------------- /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/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/src/icons/svg/link.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pkg/ui/src/layout/components/index.js: -------------------------------------------------------------------------------- 1 | export { default as AppMain } from './AppMain' 2 | export { default as Navbar } from './Navbar' 3 | export { default as Settings } from './Settings' 4 | export { default as Sidebar } from './Sidebar/index.vue' 5 | export { default as TagsView } from './TagsView/index.vue' 6 | -------------------------------------------------------------------------------- /pkg/ui/src/directive/waves/index.js: -------------------------------------------------------------------------------- 1 | import waves from './waves' 2 | 3 | const install = function(Vue) { 4 | Vue.directive('waves', waves) 5 | } 6 | 7 | if (window.Vue) { 8 | window.waves = waves 9 | Vue.use(install); // eslint-disable-line 10 | } 11 | 12 | waves.install = install 13 | export default waves 14 | -------------------------------------------------------------------------------- /scripts/tag.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # TODO modify TAG of released Naftis during CI 4 | # export TAG=$(if [ `git branch | grep \* | cut -d ' ' -f2` != "master" ]; then git checkout master --quiet; fi && git describe --tags --abbrev=0) 5 | if [[ -z $TAG ]]; then 6 | TAG=`git describe --tags --abbrev=0` 7 | fi 8 | 9 | echo $TAG -------------------------------------------------------------------------------- /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/src/views/redirect/index.vue: -------------------------------------------------------------------------------- 1 | 13 | -------------------------------------------------------------------------------- /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/ui/src/directive/el-drag-dialog/index.js: -------------------------------------------------------------------------------- 1 | import drag from './drag' 2 | 3 | const install = function(Vue) { 4 | Vue.directive('el-drag-dialog', drag) 5 | } 6 | 7 | if (window.Vue) { 8 | window['el-drag-dialog'] = drag 9 | Vue.use(install); // eslint-disable-line 10 | } 11 | 12 | drag.install = install 13 | export default drag 14 | -------------------------------------------------------------------------------- /pkg/ui/src/icons/svg/component.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pkg/ui/src/icons/svg/guide.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pkg/ui/src/icons/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import SvgIcon from '@/components/SvgIcon'// svg component 3 | 4 | // register globally 5 | Vue.component('svg-icon', SvgIcon) 6 | 7 | const req = require.context('./svg', false, /\.svg$/) 8 | const requireAll = requireContext => requireContext.keys().map(requireContext) 9 | requireAll(req) 10 | -------------------------------------------------------------------------------- /pkg/h5/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Lime 8 | 9 | 10 |
11 | 12 | 13 | -------------------------------------------------------------------------------- /pkg/h5/src/assets/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /pkg/ui/src/directive/clipboard/index.js: -------------------------------------------------------------------------------- 1 | import Clipboard from './clipboard' 2 | 3 | const install = function(Vue) { 4 | Vue.directive('Clipboard', Clipboard) 5 | } 6 | 7 | if (window.Vue) { 8 | window.clipboard = Clipboard 9 | Vue.use(install); // eslint-disable-line 10 | } 11 | 12 | Clipboard.install = install 13 | export default Clipboard 14 | -------------------------------------------------------------------------------- /pkg/ui/src/icons/svg/money.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /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/ui/src/utils/auth.js: -------------------------------------------------------------------------------- 1 | import Cookies from 'js-cookie' 2 | 3 | const TokenKey = 'Admin-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/ui/src/directive/permission/index.js: -------------------------------------------------------------------------------- 1 | import permission from './permission' 2 | 3 | const install = function(Vue) { 4 | Vue.directive('permission', permission) 5 | } 6 | 7 | if (window.Vue) { 8 | window['permission'] = permission 9 | Vue.use(install); // eslint-disable-line 10 | } 11 | 12 | permission.install = install 13 | export default permission 14 | -------------------------------------------------------------------------------- /pkg/ui/src/icons/svg/email.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /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/ui/src/icons/svg/drag.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pkg/ui/src/icons/svgo.yml: -------------------------------------------------------------------------------- 1 | # replace default config 2 | 3 | # multipass: true 4 | # full: true 5 | 6 | plugins: 7 | 8 | # - name 9 | # 10 | # or: 11 | # - name: false 12 | # - name: true 13 | # 14 | # or: 15 | # - name: 16 | # param1: 1 17 | # param2: 2 18 | 19 | - removeAttrs: 20 | attrs: 21 | - 'fill' 22 | - 'fill-rule' 23 | -------------------------------------------------------------------------------- /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/src/directive/el-table/index.js: -------------------------------------------------------------------------------- 1 | import adaptive from './adaptive' 2 | 3 | const install = function(Vue) { 4 | Vue.directive('el-height-adaptive-table', adaptive) 5 | } 6 | 7 | if (window.Vue) { 8 | window['el-height-adaptive-table'] = adaptive 9 | Vue.use(install); // eslint-disable-line 10 | } 11 | 12 | adaptive.install = install 13 | export default adaptive 14 | -------------------------------------------------------------------------------- /pkg/ui/src/api/lime-admin/comment.js: -------------------------------------------------------------------------------- 1 | import request from './request' 2 | 3 | export function CommentList() { 4 | return request({ 5 | url: '/admin/novels/comments', 6 | method: 'GET' 7 | }) 8 | } 9 | 10 | export function deleteComment(id,data) { 11 | return request({ 12 | url: '/admin/novels/comments/' + id, 13 | method: 'delete', 14 | data: data 15 | }) 16 | } 17 | -------------------------------------------------------------------------------- /pkg/ui/src/api/lime-admin/comicComment.js: -------------------------------------------------------------------------------- 1 | import request from './request' 2 | 3 | export function CommentList() { 4 | return request({ 5 | url: '/admin/comics/comments', 6 | method: 'GET' 7 | }) 8 | } 9 | 10 | export function deleteComment(id,data) { 11 | return request({ 12 | url: '/admin/comics/comments/' + id, 13 | method: 'delete', 14 | data: data 15 | }) 16 | } 17 | -------------------------------------------------------------------------------- /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/ui/src/views/users/emun/index.js: -------------------------------------------------------------------------------- 1 | export const USERS_GENDER = [ //性别 2 | { key: 1, display_name: '男' }, 3 | { key: 2, display_name: '女' } 4 | ] 5 | 6 | export const USERS_STATUS = [ //用户状态 7 | { key: 0, display_name: '正常' }, 8 | { key: 1, display_name: '封禁' } 9 | ] 10 | 11 | export const USERS_ROBOTS = [ //是否免登 12 | { key: 0, display_name: '是' }, 13 | { key: 1, display_name: '否' } 14 | ] 15 | -------------------------------------------------------------------------------- /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/h5/src/App.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 12 | 13 | 21 | -------------------------------------------------------------------------------- /pkg/ui/src/views/login/auth-redirect.vue: -------------------------------------------------------------------------------- 1 | 16 | -------------------------------------------------------------------------------- /pkg/ui/plopfile.js: -------------------------------------------------------------------------------- 1 | const viewGenerator = require('./plop-templates/view/prompt') 2 | const componentGenerator = require('./plop-templates/component/prompt') 3 | const storeGenerator = require('./plop-templates/store/prompt.js') 4 | 5 | module.exports = function(plop) { 6 | plop.setGenerator('view', viewGenerator) 7 | plop.setGenerator('component', componentGenerator) 8 | plop.setGenerator('store', storeGenerator) 9 | } 10 | -------------------------------------------------------------------------------- /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/ui/src/icons/svg/documentation.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pkg/ui/src/icons/svg/fullscreen.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /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/ui/plop-templates/view/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/src/icons/svg/lock.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pkg/ui/src/icons/svg/user.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /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/src/icons/svg/excel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pkg/ui/src/views/permission/page.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 20 | -------------------------------------------------------------------------------- /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/ui/src/icons/svg/example.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /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/ui/src/api/auth-admin/auth.js: -------------------------------------------------------------------------------- 1 | import request from './request' 2 | 3 | /** 4 | * 获取授权的菜单接口 5 | */ 6 | export function getMenu() { 7 | return request({ 8 | url: 'v1/user/menu', 9 | method: 'GET', 10 | params: { 11 | domain: 'lime' 12 | } 13 | }) 14 | } 15 | 16 | /** 17 | * 获取用户权限列表接口 18 | */ 19 | export function permList() { 20 | return request({ 21 | url: 'v1/users/perm/list', 22 | method: 'GET', 23 | params: { 24 | code: 'lime' 25 | } 26 | }) 27 | } 28 | 29 | -------------------------------------------------------------------------------- /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/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/ui/src/components/Tinymce/toolbar.js: -------------------------------------------------------------------------------- 1 | // Here is a list of the toolbar 2 | // Detail list see https://www.tinymce.com/docs/advanced/editor-control-identifiers/#toolbarcontrols 3 | 4 | const toolbar = ['searchreplace bold italic underline strikethrough alignleft aligncenter alignright outdent indent blockquote undo redo removeformat subscript superscript code codesample', 'hr bullist numlist link image charmap preview anchor pagebreak insertdatetime media table emoticons forecolor backcolor fullscreen'] 5 | 6 | export default toolbar 7 | -------------------------------------------------------------------------------- /pkg/ui/src/api/user.js: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request' 2 | 3 | export function login(data) { 4 | return request({ 5 | url: '/vue-element-admin/user/login', 6 | method: 'post', 7 | data 8 | }) 9 | } 10 | 11 | export function getInfo(token) { 12 | return request({ 13 | url: '/vue-element-admin/user/info', 14 | method: 'get', 15 | params: { token } 16 | }) 17 | } 18 | 19 | export function logout() { 20 | return request({ 21 | url: '/vue-element-admin/user/logout', 22 | method: 'post' 23 | }) 24 | } 25 | -------------------------------------------------------------------------------- /pkg/ui/src/store/modules/errorLog.js: -------------------------------------------------------------------------------- 1 | const state = { 2 | logs: [] 3 | } 4 | 5 | const mutations = { 6 | ADD_ERROR_LOG: (state, log) => { 7 | state.logs.push(log) 8 | }, 9 | CLEAR_ERROR_LOG: (state) => { 10 | state.logs.splice(0) 11 | } 12 | } 13 | 14 | const actions = { 15 | addErrorLog({ commit }, log) { 16 | commit('ADD_ERROR_LOG', log) 17 | }, 18 | clearErrorLog({ commit }) { 19 | commit('CLEAR_ERROR_LOG') 20 | } 21 | } 22 | 23 | export default { 24 | namespaced: true, 25 | state, 26 | mutations, 27 | actions 28 | } 29 | -------------------------------------------------------------------------------- /pkg/ui/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | <%= webpackConfig.name %> 10 | 11 | 12 |
13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /pkg/ui/src/icons/svg/star.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /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/ui/src/api/lime-admin/setting.js: -------------------------------------------------------------------------------- 1 | import request from './request' 2 | 3 | export function wechatSetting(code, data) { 4 | return request({ 5 | url: '/admin/configs/' + code + '/distributor', 6 | method: 'post', 7 | data: data 8 | }) 9 | } 10 | 11 | export function getWetchatSetting(code, data) { 12 | return request({ 13 | url: '/admin/distributor/' + code, 14 | method: 'GET' 15 | }) 16 | } 17 | 18 | export function getTokenAndKey() { 19 | return request({ 20 | url: '/admin/setting/getTokenAndEncodingAESKey', 21 | method: 'GET' 22 | }) 23 | } -------------------------------------------------------------------------------- /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/ui/src/icons/svg/table.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pkg/ui/src/icons/svg/search.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pkg/ui/src/components/Tinymce/plugins.js: -------------------------------------------------------------------------------- 1 | // Any plugins you want to use has to be imported 2 | // Detail plugins list see https://www.tinymce.com/docs/plugins/ 3 | // Custom builds see https://www.tinymce.com/download/custom-builds/ 4 | 5 | const plugins = ['advlist anchor autolink autosave code codesample colorpicker colorpicker contextmenu directionality emoticons fullscreen hr image imagetools insertdatetime link lists media nonbreaking noneditable pagebreak paste preview print save searchreplace spellchecker tabfocus table template textcolor textpattern visualblocks visualchars wordcount'] 6 | 7 | export default plugins 8 | -------------------------------------------------------------------------------- /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/ui/src/store/getters.js: -------------------------------------------------------------------------------- 1 | const getters = { 2 | sidebar: state => state.app.sidebar, 3 | size: state => state.app.size, 4 | device: state => state.app.device, 5 | visitedViews: state => state.tagsView.visitedViews, 6 | cachedViews: state => state.tagsView.cachedViews, 7 | token: state => state.user.token, 8 | avatar: state => state.user.avatar, 9 | name: state => state.user.name, 10 | introduction: state => state.user.introduction, 11 | roles: state => state.user.roles, 12 | permission_routes: state => state.permission.routes, 13 | errorLogs: state => state.errorLog.logs 14 | } 15 | export default getters 16 | -------------------------------------------------------------------------------- /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/ui/src/icons/svg/password.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pkg/ui/src/components/ImageCropper/utils/data2blob.js: -------------------------------------------------------------------------------- 1 | /** 2 | * database64文件格式转换为2进制 3 | * 4 | * @param {[String]} data dataURL 的格式为 “data:image/png;base64,****”,逗号之前都是一些说明性的文字,我们只需要逗号之后的就行了 5 | * @param {[String]} mime [description] 6 | * @return {[blob]} [description] 7 | */ 8 | export default function(data, mime) { 9 | data = data.split(',')[1] 10 | data = window.atob(data) 11 | var ia = new Uint8Array(data.length) 12 | for (var i = 0; i < data.length; i++) { 13 | ia[i] = data.charCodeAt(i) 14 | } 15 | // canvas.toDataURL 返回的默认格式就是 image/png 16 | return new Blob([ia], { 17 | type: mime 18 | }) 19 | } 20 | -------------------------------------------------------------------------------- /pkg/ui/src/icons/svg/education.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pkg/ui/src/layout/components/Sidebar/Item.vue: -------------------------------------------------------------------------------- 1 | 30 | -------------------------------------------------------------------------------- /pkg/ui/src/views/dashboard/index.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 26 | 35 | -------------------------------------------------------------------------------- /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/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/ui/src/icons/svg/tab.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /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/ui/src/api/auth-admin/user.js: -------------------------------------------------------------------------------- 1 | import request from './request' 2 | // import qs from 'qs' 3 | 4 | /** 5 | * 获取用户信息接口 6 | */ 7 | export function getInfo() { 8 | return request({ 9 | url: 'v1/users/info', 10 | method: 'GET', 11 | params: { 12 | domain: 'zeus-config' 13 | } 14 | }) 15 | } 16 | 17 | export function login(data) { 18 | return request({ 19 | url: 'v1/users/login', 20 | method: 'POST', 21 | data 22 | }) 23 | } 24 | 25 | export function logout() { 26 | return request({ 27 | url: 'v1/users/logout', 28 | method: 'GET', 29 | params: { 30 | domain: 'zeus-config' 31 | } 32 | }) 33 | } 34 | 35 | -------------------------------------------------------------------------------- /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/ui/src/icons/svg/message.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /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/ui/src/directive/permission/permission.js: -------------------------------------------------------------------------------- 1 | import store from '@/store' 2 | 3 | export default { 4 | inserted(el, binding, vnode) { 5 | const { value } = binding 6 | const roles = store.getters && store.getters.roles 7 | 8 | if (value && value instanceof Array && value.length > 0) { 9 | const permissionRoles = value 10 | 11 | const hasPermission = roles.some(role => { 12 | return permissionRoles.includes(role) 13 | }) 14 | 15 | if (!hasPermission) { 16 | el.parentNode && el.parentNode.removeChild(el) 17 | } 18 | } else { 19 | throw new Error(`need roles! Like v-permission="['admin','editor']"`) 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /pkg/ui/src/icons/svg/theme.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /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/ui/src/vendor/Export2Zip.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | import { saveAs } from 'file-saver' 3 | import JSZip from 'jszip' 4 | 5 | export function export_txt_to_zip(th, jsonData, txtName, zipName) { 6 | const zip = new JSZip() 7 | const txt_name = txtName || 'file' 8 | const zip_name = zipName || 'file' 9 | const data = jsonData 10 | let txtData = `${th}\r\n` 11 | data.forEach((row) => { 12 | let tempStr = '' 13 | tempStr = row.toString() 14 | txtData += `${tempStr}\r\n` 15 | }) 16 | zip.file(`${txt_name}.txt`, txtData) 17 | zip.generateAsync({ 18 | type: "blob" 19 | }).then((blob) => { 20 | saveAs(blob, `${zip_name}.zip`) 21 | }, (err) => { 22 | alert('导出失败') 23 | }) 24 | } 25 | -------------------------------------------------------------------------------- /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/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/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/ui/src/icons/svg/peoples.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pkg/ui/src/utils/permission.js: -------------------------------------------------------------------------------- 1 | import store from '@/store' 2 | 3 | /** 4 | * @param {Array} value 5 | * @returns {Boolean} 6 | * @example see @/views/permission/directive.vue 7 | */ 8 | export default function checkPermission(value) { 9 | if (value && value instanceof Array && value.length > 0) { 10 | const roles = store.getters && store.getters.roles 11 | const permissionRoles = value 12 | 13 | const hasPermission = roles.some(role => { 14 | return permissionRoles.includes(role) 15 | }) 16 | 17 | if (!hasPermission) { 18 | return false 19 | } 20 | return true 21 | } else { 22 | console.error(`need roles! Like v-permission="['admin','editor']"`) 23 | return false 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /pkg/ui/src/components/MarkdownEditor/default-options.js: -------------------------------------------------------------------------------- 1 | // doc: https://nhnent.github.io/tui.editor/api/latest/ToastUIEditor.html#ToastUIEditor 2 | export default { 3 | minHeight: '200px', 4 | previewStyle: 'vertical', 5 | useCommandShortcut: true, 6 | useDefaultHTMLSanitizer: true, 7 | usageStatistics: false, 8 | hideModeSwitch: false, 9 | toolbarItems: [ 10 | 'heading', 11 | 'bold', 12 | 'italic', 13 | 'strike', 14 | 'divider', 15 | 'hr', 16 | 'quote', 17 | 'divider', 18 | 'ul', 19 | 'ol', 20 | 'task', 21 | 'indent', 22 | 'outdent', 23 | 'divider', 24 | 'table', 25 | 'image', 26 | 'link', 27 | 'divider', 28 | 'code', 29 | 'codeblock' 30 | ] 31 | } 32 | -------------------------------------------------------------------------------- /pkg/ui/src/api/lime-admin/category.js: -------------------------------------------------------------------------------- 1 | import request from './request' 2 | 3 | export function categoryList() { 4 | return request({ 5 | url: '/admin/novels/categories', 6 | method: 'GET' 7 | }) 8 | } 9 | 10 | export function updateCategory(id, data) { 11 | return request({ 12 | url: '/admin/novels/categories/' + id, 13 | method: 'put', 14 | data: data 15 | }) 16 | } 17 | 18 | export function createCategory(data) { 19 | return request({ 20 | url: '/admin/novels/categories', 21 | method: 'post', 22 | data: data 23 | }) 24 | } 25 | 26 | export function deleteCategory(id,data) { 27 | return request({ 28 | url: '/novels/categories/' + id, 29 | method: 'delete', 30 | data: data 31 | }) 32 | } 33 | -------------------------------------------------------------------------------- /pkg/ui/tests/unit/components/Hamburger.spec.js: -------------------------------------------------------------------------------- 1 | import { shallowMount } from '@vue/test-utils' 2 | import Hamburger from '@/components/Hamburger/index.vue' 3 | describe('Hamburger.vue', () => { 4 | it('toggle click', () => { 5 | const wrapper = shallowMount(Hamburger) 6 | const mockFn = jest.fn() 7 | wrapper.vm.$on('toggleClick', mockFn) 8 | wrapper.find('.hamburger').trigger('click') 9 | expect(mockFn).toBeCalled() 10 | }) 11 | it('prop isActive', () => { 12 | const wrapper = shallowMount(Hamburger) 13 | wrapper.setProps({ isActive: true }) 14 | expect(wrapper.contains('.is-active')).toBe(true) 15 | wrapper.setProps({ isActive: false }) 16 | expect(wrapper.contains('.is-active')).toBe(false) 17 | }) 18 | }) 19 | -------------------------------------------------------------------------------- /pkg/ui/tests/unit/components/SvgIcon.spec.js: -------------------------------------------------------------------------------- 1 | import { shallowMount } from '@vue/test-utils' 2 | import SvgIcon from '@/components/SvgIcon/index.vue' 3 | describe('SvgIcon.vue', () => { 4 | it('iconClass', () => { 5 | const wrapper = shallowMount(SvgIcon, { 6 | propsData: { 7 | iconClass: 'test' 8 | } 9 | }) 10 | expect(wrapper.find('use').attributes().href).toBe('#icon-test') 11 | }) 12 | it('className', () => { 13 | const wrapper = shallowMount(SvgIcon, { 14 | propsData: { 15 | iconClass: 'test' 16 | } 17 | }) 18 | expect(wrapper.classes().length).toBe(1) 19 | wrapper.setProps({ className: 'test' }) 20 | expect(wrapper.classes().includes('test')).toBe(true) 21 | }) 22 | }) 23 | -------------------------------------------------------------------------------- /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/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/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/ui/src/api/lime-admin/comicCategory.js: -------------------------------------------------------------------------------- 1 | import request from './request' 2 | 3 | export function categoryList() { 4 | return request({ 5 | url: '/admin/comics/categories', 6 | method: 'GET' 7 | }) 8 | } 9 | 10 | export function updateCategory(id, data) { 11 | return request({ 12 | url: '/admin/comics/categories/' + id, 13 | method: 'put', 14 | data: data 15 | }) 16 | } 17 | 18 | export function createCategory(data) { 19 | return request({ 20 | url: '/admin/comics/categories', 21 | method: 'post', 22 | data: data 23 | }) 24 | } 25 | 26 | export function deleteCategory(id,data) { 27 | return request({ 28 | url: '/admin/comics/categories/' + id, 29 | method: 'delete', 30 | data: data 31 | }) 32 | } 33 | -------------------------------------------------------------------------------- /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/ui/src/layout/components/Sidebar/FixiOSBug.js: -------------------------------------------------------------------------------- 1 | export default { 2 | computed: { 3 | device() { 4 | return this.$store.state.app.device 5 | } 6 | }, 7 | mounted() { 8 | // In order to fix the click on menu on the ios device will trigger the mouseleave bug 9 | // https://github.com/PanJiaChen/vue-element-admin/issues/1135 10 | this.fixBugIniOS() 11 | }, 12 | methods: { 13 | fixBugIniOS() { 14 | const $subMenu = this.$refs.subMenu 15 | if ($subMenu) { 16 | const handleMouseleave = $subMenu.handleMouseleave 17 | $subMenu.handleMouseleave = (e) => { 18 | if (this.device === 'mobile') { 19 | return 20 | } 21 | handleMouseleave(e) 22 | } 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /pkg/ui/src/utils/clipboard.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Clipboard from 'clipboard' 3 | 4 | function clipboardSuccess() { 5 | Vue.prototype.$message({ 6 | message: 'Copy successfully', 7 | type: 'success', 8 | duration: 1500 9 | }) 10 | } 11 | 12 | function clipboardError() { 13 | Vue.prototype.$message({ 14 | message: 'Copy failed', 15 | type: 'error' 16 | }) 17 | } 18 | 19 | export default function handleClipboard(text, event) { 20 | const clipboard = new Clipboard(event.target, { 21 | text: () => text 22 | }) 23 | clipboard.on('success', () => { 24 | clipboardSuccess() 25 | clipboard.destroy() 26 | }) 27 | clipboard.on('error', () => { 28 | clipboardError() 29 | clipboard.destroy() 30 | }) 31 | clipboard.onClick(event) 32 | } 33 | -------------------------------------------------------------------------------- /pkg/ui/src/views/comics/emun/index.js: -------------------------------------------------------------------------------- 1 | export const CATEGORY_CHANNEL = [ 2 | { key: 1, display_name: '男频' }, 3 | { key: 2, display_name: '女频' } 4 | ] 5 | 6 | export const COMIC_ATTRS = [ //属性 7 | { key: 0, display_name: '免费' }, 8 | { key: 1, display_name: '热门' }, 9 | { key: 2, display_name: '会员' }, 10 | { key: 3, display_name: '推荐' } 11 | ] 12 | 13 | export const COMIC_ONLINE_STATUS = [ //上架状态 14 | { key: 0, display_name: '已经上架' }, 15 | { key: 1, display_name: '已经下架' } 16 | ] 17 | 18 | export const COMIC_STATUS = [ //小说状态 19 | { key: 0, display_name: '连载中' }, 20 | { key: 1, display_name: '已完结' }, 21 | { key: 2, display_name: '太监' } 22 | ] 23 | 24 | export const COMIC_IS_SENSITIVITYS = [ //是否敏感 25 | { key: 0, display_name: '不敏感' }, 26 | { key: 1, display_name: '敏感' } 27 | ] 28 | -------------------------------------------------------------------------------- /pkg/ui/src/views/permission/components/SwitchRoles.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 33 | -------------------------------------------------------------------------------- /pkg/h5/src/assets/lock.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pkg/ui/src/store/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Vuex from 'vuex' 3 | import getters from './getters' 4 | 5 | Vue.use(Vuex) 6 | 7 | // https://webpack.js.org/guides/dependency-management/#requirecontext 8 | const modulesFiles = require.context('./modules', true, /\.js$/) 9 | 10 | // you do not need `import app from './modules/app'` 11 | // it will auto require all vuex module from modules file 12 | const modules = modulesFiles.keys().reduce((modules, modulePath) => { 13 | // set './app.js' => 'app' 14 | const moduleName = modulePath.replace(/^\.\/(.*)\.\w+$/, '$1') 15 | const value = modulesFiles(modulePath) 16 | modules[moduleName] = value.default 17 | return modules 18 | }, {}) 19 | 20 | const store = new Vuex.Store({ 21 | modules, 22 | getters 23 | }) 24 | 25 | export default store 26 | -------------------------------------------------------------------------------- /pkg/ui/src/icons/svg/edit.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pkg/ui/src/store/modules/settings.js: -------------------------------------------------------------------------------- 1 | import variables from '@/styles/element-variables.scss' 2 | import defaultSettings from '@/settings' 3 | 4 | const { showSettings, tagsView, fixedHeader, sidebarLogo } = defaultSettings 5 | 6 | const state = { 7 | theme: variables.theme, 8 | showSettings: showSettings, 9 | tagsView: tagsView, 10 | fixedHeader: fixedHeader, 11 | sidebarLogo: sidebarLogo 12 | } 13 | 14 | const mutations = { 15 | CHANGE_SETTING: (state, { key, value }) => { 16 | if (state.hasOwnProperty(key)) { 17 | state[key] = value 18 | } 19 | } 20 | } 21 | 22 | const actions = { 23 | changeSetting({ commit }, data) { 24 | commit('CHANGE_SETTING', data) 25 | } 26 | } 27 | 28 | export default { 29 | namespaced: true, 30 | state, 31 | mutations, 32 | actions 33 | } 34 | 35 | -------------------------------------------------------------------------------- /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/ui/src/icons/svg/nested.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /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/h5/src/components/common/HorizontalList.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 25 | 26 | 29 | -------------------------------------------------------------------------------- /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/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/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/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/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/ui/src/layout/components/Sidebar/Link.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 44 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/ui/src/icons/svg/tree-table.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pkg/ui/src/views/books/emun/index.js: -------------------------------------------------------------------------------- 1 | export const CATEGORY_CHANNEL = [ 2 | { key: 0, display_name: '全部' }, 3 | { key: 1, display_name: '男生' }, 4 | { key: 2, display_name: '女生' } 5 | ] 6 | 7 | export const BOOK_ATTRS = [ //属性 8 | { key: 0, display_name: '新书' }, 9 | { key: 1, display_name: '热门' }, 10 | { key: 2, display_name: '会员' }, 11 | { key: 3, display_name: '爽文' }, 12 | { key: 4, display_name: '精选' }, 13 | { key: 5, display_name: '大神' } 14 | ] 15 | 16 | export const BOOK_ONLINE_STATUS = [ //上架状态 17 | { key: 0, display_name: '已经上架' }, 18 | { key: 1, display_name: '已经下架' } 19 | ] 20 | 21 | export const BOOK_STATUS = [ //小说状态 22 | { key: 0, display_name: '连载中' }, 23 | { key: 1, display_name: '已完结' }, 24 | { key: 2, display_name: '太监' } 25 | ] 26 | 27 | export const BOOK_IS_SENSITIVITYS = [ //是否敏感 28 | { key: 0, display_name: '不敏感' }, 29 | { key: 1, display_name: '敏感' } 30 | ] 31 | -------------------------------------------------------------------------------- /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/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/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/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/ui/src/icons/svg/eye.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /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/ui/src/styles/variables.scss: -------------------------------------------------------------------------------- 1 | // base color 2 | $blue:#324157; 3 | $light-blue:#3A71A8; 4 | $red:#C03639; 5 | $pink: #E65D6E; 6 | $green: #30B08F; 7 | $tiffany: #4AB7BD; 8 | $yellow:#FEC171; 9 | $panGreen: #30B08F; 10 | 11 | // sidebar 12 | $menuText:#bfcbd9; 13 | $menuActiveText:#409EFF; 14 | $subMenuActiveText:#f4f4f5; // https://github.com/ElemeFE/element/issues/12951 15 | 16 | $menuBg:#304156; 17 | $menuHover:#263445; 18 | 19 | $subMenuBg:#1f2d3d; 20 | $subMenuHover:#001528; 21 | 22 | $sideBarWidth: 210px; 23 | 24 | // the :export directive is the magic sauce for webpack 25 | // https://www.bluematador.com/blog/how-to-share-variables-between-js-and-sass 26 | :export { 27 | menuText: $menuText; 28 | menuActiveText: $menuActiveText; 29 | subMenuActiveText: $subMenuActiveText; 30 | menuBg: $menuBg; 31 | menuHover: $menuHover; 32 | subMenuBg: $subMenuBg; 33 | subMenuHover: $subMenuHover; 34 | sideBarWidth: $sideBarWidth; 35 | } 36 | -------------------------------------------------------------------------------- /pkg/ui/src/directive/waves/waves.css: -------------------------------------------------------------------------------- 1 | .waves-ripple { 2 | position: absolute; 3 | border-radius: 100%; 4 | background-color: rgba(0, 0, 0, 0.15); 5 | background-clip: padding-box; 6 | pointer-events: none; 7 | -webkit-user-select: none; 8 | -moz-user-select: none; 9 | -ms-user-select: none; 10 | user-select: none; 11 | -webkit-transform: scale(0); 12 | -ms-transform: scale(0); 13 | transform: scale(0); 14 | opacity: 1; 15 | } 16 | 17 | .waves-ripple.z-active { 18 | opacity: 0; 19 | -webkit-transform: scale(2); 20 | -ms-transform: scale(2); 21 | transform: scale(2); 22 | -webkit-transition: opacity 1.2s ease-out, -webkit-transform 0.6s ease-out; 23 | transition: opacity 1.2s ease-out, -webkit-transform 0.6s ease-out; 24 | transition: opacity 1.2s ease-out, transform 0.6s ease-out; 25 | transition: opacity 1.2s ease-out, transform 0.6s ease-out, -webkit-transform 0.6s ease-out; 26 | } -------------------------------------------------------------------------------- /pkg/ui/src/styles/element-variables.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * I think element-ui's default theme color is too light for long-term use. 3 | * So I modified the default color and you can modify it to your liking. 4 | **/ 5 | 6 | /* theme color */ 7 | $--color-primary: #1890ff; 8 | $--color-success: #13ce66; 9 | $--color-warning: #ffba00; 10 | $--color-danger: #ff4949; 11 | // $--color-info: #1E1E1E; 12 | 13 | $--button-font-weight: 400; 14 | 15 | // $--color-text-regular: #1f2d3d; 16 | 17 | $--border-color-light: #dfe4ed; 18 | $--border-color-lighter: #e6ebf5; 19 | 20 | $--table-border: 1px solid #dfe6ec; 21 | 22 | /* icon font path, required */ 23 | $--font-path: "~element-ui/lib/theme-chalk/fonts"; 24 | 25 | @import "~element-ui/packages/theme-chalk/src/index"; 26 | 27 | // the :export directive is the magic sauce for webpack 28 | // https://www.bluematador.com/blog/how-to-share-variables-between-js-and-sass 29 | :export { 30 | theme: $--color-primary; 31 | } 32 | -------------------------------------------------------------------------------- /pkg/ui/src/styles/transition.scss: -------------------------------------------------------------------------------- 1 | // global transition css 2 | 3 | /* fade */ 4 | .fade-enter-active, 5 | .fade-leave-active { 6 | transition: opacity 0.28s; 7 | } 8 | 9 | .fade-enter, 10 | .fade-leave-active { 11 | opacity: 0; 12 | } 13 | 14 | /* fade-transform */ 15 | .fade-transform-leave-active, 16 | .fade-transform-enter-active { 17 | transition: all .5s; 18 | } 19 | 20 | .fade-transform-enter { 21 | opacity: 0; 22 | transform: translateX(-30px); 23 | } 24 | 25 | .fade-transform-leave-to { 26 | opacity: 0; 27 | transform: translateX(30px); 28 | } 29 | 30 | /* breadcrumb transition */ 31 | .breadcrumb-enter-active, 32 | .breadcrumb-leave-active { 33 | transition: all .5s; 34 | } 35 | 36 | .breadcrumb-enter, 37 | .breadcrumb-leave-active { 38 | opacity: 0; 39 | transform: translateX(20px); 40 | } 41 | 42 | .breadcrumb-move { 43 | transition: all .5s; 44 | } 45 | 46 | .breadcrumb-leave-active { 47 | position: absolute; 48 | } 49 | -------------------------------------------------------------------------------- /pkg/ui/src/settings.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | title: 'Lime Admin', 3 | 4 | /** 5 | * @type {boolean} true | false 6 | * @description Whether show the settings right-panel 7 | */ 8 | showSettings: false, 9 | 10 | /** 11 | * @type {boolean} true | false 12 | * @description Whether need tagsView 13 | */ 14 | tagsView: false, 15 | 16 | /** 17 | * @type {boolean} true | false 18 | * @description Whether fix the header 19 | */ 20 | fixedHeader: false, 21 | 22 | /** 23 | * @type {boolean} true | false 24 | * @description Whether show the logo in sidebar 25 | */ 26 | sidebarLogo: true, 27 | 28 | /** 29 | * @type {string | array} 'production' | ['production', 'development'] 30 | * @description Need show err logs component. 31 | * The default is only used in the production env 32 | * If you want to also use it in dev, you can pass ['production', 'development'] 33 | */ 34 | errorLog: 'production' 35 | } 36 | -------------------------------------------------------------------------------- /pkg/ui/src/icons/svg/clipboard.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /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/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/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/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/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/ui/src/icons/svg/list.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /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/src/components/common/Similar.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 36 | 37 | 40 | -------------------------------------------------------------------------------- /pkg/ui/src/views/guide/index.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 37 | -------------------------------------------------------------------------------- /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/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/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/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/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/ui/src/icons/svg/icon.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /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/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/ui/src/utils/error-log.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import store from '@/store' 3 | import { isString, isArray } from '@/utils/validate' 4 | import settings from '@/settings' 5 | 6 | // you can set in settings.js 7 | // errorLog:'production' | ['production', 'development'] 8 | const { errorLog: needErrorLog } = settings 9 | 10 | function checkNeed() { 11 | const env = process.env.NODE_ENV 12 | if (isString(needErrorLog)) { 13 | return env === needErrorLog 14 | } 15 | if (isArray(needErrorLog)) { 16 | return needErrorLog.includes(env) 17 | } 18 | return false 19 | } 20 | 21 | if (checkNeed()) { 22 | Vue.config.errorHandler = function(err, vm, info, a) { 23 | // Don't ask me why I use Vue.nextTick, it just a hack. 24 | // detail see https://forum.vuejs.org/t/dispatch-in-vue-config-errorhandler-has-some-problem/23500 25 | Vue.nextTick(() => { 26 | store.dispatch('errorLog/addErrorLog', { 27 | err, 28 | vm, 29 | info, 30 | url: window.location.href 31 | }) 32 | console.error(err, info) 33 | }) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /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/ui/src/api/lime-admin/chapter.js: -------------------------------------------------------------------------------- 1 | import request from './request' 2 | 3 | export function getChapter(id) { 4 | return request({ 5 | url: '/admin/novels/chapters/' + id, 6 | method: 'GET' 7 | }) 8 | } 9 | 10 | export function ChapterList(query) { 11 | return request({ 12 | url: '/admin/novels/chapters', 13 | method: 'GET', 14 | params: query 15 | }) 16 | } 17 | 18 | export function updateChapter(id, data) { 19 | return request({ 20 | url: '/admin/novels/chapters/' + id, 21 | method: 'put', 22 | data: data 23 | }) 24 | } 25 | 26 | export function updatestatus(id, data) { 27 | return request({ 28 | url: '/admin/novels//chapters/' + id + '/status', 29 | method: 'post', 30 | data: data 31 | }) 32 | } 33 | 34 | export function createChapter(data) { 35 | return request({ 36 | url: '/admin/novels/chapters', 37 | method: 'post', 38 | data: data 39 | }) 40 | } 41 | 42 | export function deleteChapter(id,data) { 43 | return request({ 44 | url: '/admin/novels/chapters/' + id, 45 | method: 'delete', 46 | data: data 47 | }) 48 | } 49 | -------------------------------------------------------------------------------- /pkg/ui/src/api/lime-admin/comicChapter.js: -------------------------------------------------------------------------------- 1 | import request from './request' 2 | 3 | export function getChapter(id) { 4 | return request({ 5 | url: '/admin/comics/chapters/' + id, 6 | method: 'GET' 7 | }) 8 | } 9 | 10 | export function ChapterList(query) { 11 | return request({ 12 | url: '/admin/comics/chapters', 13 | method: 'GET', 14 | params: query 15 | }) 16 | } 17 | 18 | export function updateChapter(id, data) { 19 | return request({ 20 | url: '/admin/comics/chapters/' + id, 21 | method: 'put', 22 | data: data 23 | }) 24 | } 25 | 26 | export function updatestatus(id, data) { 27 | return request({ 28 | url: '/admin/comics//chapters/' + id + '/status', 29 | method: 'post', 30 | data: data 31 | }) 32 | } 33 | 34 | export function createChapter(data) { 35 | return request({ 36 | url: '/admin/comics/chapters', 37 | method: 'post', 38 | data: data 39 | }) 40 | } 41 | 42 | export function deleteChapter(id,data) { 43 | return request({ 44 | url: '/admin/comics/chapters/' + id, 45 | method: 'delete', 46 | data: data 47 | }) 48 | } 49 | -------------------------------------------------------------------------------- /pkg/ui/src/api/lime-admin/users.js: -------------------------------------------------------------------------------- 1 | import request from './request' 2 | 3 | export function getUser(id) { 4 | return request({ 5 | url: '/admin/users/' + id, 6 | method: 'GET' 7 | }) 8 | } 9 | 10 | export function userList(query) { 11 | return request({ 12 | url: '/admin/users', 13 | method: 'GET', 14 | params: query 15 | }) 16 | } 17 | 18 | export function updateUser(id, data) { 19 | return request({ 20 | url: '/admin/users/' + id, 21 | method: 'put', 22 | data: data 23 | }) 24 | } 25 | 26 | export function updatestatus(id, data) { 27 | return request({ 28 | url: '/admin/users/' + id + '/status', 29 | method: 'post', 30 | data: data 31 | }) 32 | } 33 | 34 | export function createUser(data) { 35 | return request({ 36 | url: '/admin/users', 37 | method: 'post', 38 | data: data 39 | }) 40 | } 41 | 42 | export function uploadAvatar(formData) { 43 | return request({ 44 | url: '/admin/upload/face', 45 | method: 'post', 46 | data: formData, 47 | headers: { 48 | 'Content-Type': 'multipart/form-data' 49 | } 50 | }) 51 | } 52 | -------------------------------------------------------------------------------- /pkg/ui/src/icons/svg/international.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /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/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/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/src/icons/svg/wechat.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pkg/ui/src/api/lime-admin/distributorLevel.js: -------------------------------------------------------------------------------- 1 | import request from './request' 2 | 3 | export function getDistributorLevel(id) { 4 | return request({ 5 | url: '/admin/distributorlevels/' + id, 6 | method: 'GET' 7 | }) 8 | } 9 | 10 | export function DistributorLevelList(query) { 11 | return request({ 12 | url: '/admin/distributorlevels', 13 | method: 'GET', 14 | params: query 15 | }) 16 | } 17 | 18 | export function updateDistributorLevel(id, data) { 19 | return request({ 20 | url: '/admin/distributorlevels/' + id, 21 | method: 'put', 22 | data: data 23 | }) 24 | } 25 | 26 | export function updatestatus(id, data) { 27 | return request({ 28 | url: '/admin/distributorlevels/' + id + '/status', 29 | method: 'post', 30 | data: data 31 | }) 32 | } 33 | 34 | export function createDistributorLevel(data) { 35 | return request({ 36 | url: '/admin/distributorlevels', 37 | method: 'post', 38 | data: data 39 | }) 40 | } 41 | 42 | export function deleteDistributorLevel(id,data) { 43 | return request({ 44 | url: '/admin/distributorlevels/' + id, 45 | method: 'delete', 46 | data: data 47 | }) 48 | } 49 | -------------------------------------------------------------------------------- /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: "" -------------------------------------------------------------------------------- /pkg/ui/src/icons/svg/skill.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /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/ui/tests/unit/utils/formatTime.spec.js: -------------------------------------------------------------------------------- 1 | import { formatTime } from '@/utils/index.js' 2 | describe('Utils:formatTime', () => { 3 | const d = new Date('2018-07-13 17:54:01') // "2018-07-13 17:54:01" 4 | const retrofit = 5 * 1000 5 | 6 | it('ten digits timestamp', () => { 7 | expect(formatTime((d / 1000).toFixed(0))).toBe('7月13日17时54分') 8 | }) 9 | it('test now', () => { 10 | expect(formatTime(+new Date() - 1)).toBe('刚刚') 11 | }) 12 | it('less two minute', () => { 13 | expect(formatTime(+new Date() - 60 * 2 * 1000 + retrofit)).toBe('2分钟前') 14 | }) 15 | it('less two hour', () => { 16 | expect(formatTime(+new Date() - 60 * 60 * 2 * 1000 + retrofit)).toBe('2小时前') 17 | }) 18 | it('less one day', () => { 19 | expect(formatTime(+new Date() - 60 * 60 * 24 * 1 * 1000)).toBe('1天前') 20 | }) 21 | it('more than one day', () => { 22 | expect(formatTime(d)).toBe('7月13日17时54分') 23 | }) 24 | it('format', () => { 25 | expect(formatTime(d, '{y}-{m}-{d} {h}:{i}')).toBe('2018-07-13 17:54') 26 | expect(formatTime(d, '{y}-{m}-{d}')).toBe('2018-07-13') 27 | expect(formatTime(d, '{y}/{m}/{d} {h}-{i}')).toBe('2018/07/13 17-54') 28 | }) 29 | }) 30 | -------------------------------------------------------------------------------- /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/ui/src/icons/svg/people.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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/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/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/ui/src/icons/svg/language.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pkg/ui/src/layout/components/AppMain.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 24 | 25 | 49 | 50 | 58 | -------------------------------------------------------------------------------- /pkg/ui/tests/unit/utils/validate.spec.js: -------------------------------------------------------------------------------- 1 | import { validUsername, validURL, validLowerCase, validUpperCase, validAlphabets } from '@/utils/validate.js' 2 | describe('Utils:validate', () => { 3 | it('validUsername', () => { 4 | expect(validUsername('admin')).toBe(true) 5 | expect(validUsername('editor')).toBe(true) 6 | expect(validUsername('xxxx')).toBe(false) 7 | }) 8 | it('validURL', () => { 9 | expect(validURL('https://github.com/PanJiaChen/vue-element-admin')).toBe(true) 10 | expect(validURL('http://github.com/PanJiaChen/vue-element-admin')).toBe(true) 11 | expect(validURL('github.com/PanJiaChen/vue-element-admin')).toBe(false) 12 | }) 13 | it('validLowerCase', () => { 14 | expect(validLowerCase('abc')).toBe(true) 15 | expect(validLowerCase('Abc')).toBe(false) 16 | expect(validLowerCase('123abc')).toBe(false) 17 | }) 18 | it('validUpperCase', () => { 19 | expect(validUpperCase('ABC')).toBe(true) 20 | expect(validUpperCase('Abc')).toBe(false) 21 | expect(validUpperCase('123ABC')).toBe(false) 22 | }) 23 | it('validAlphabets', () => { 24 | expect(validAlphabets('ABC')).toBe(true) 25 | expect(validAlphabets('Abc')).toBe(true) 26 | expect(validAlphabets('123aBC')).toBe(false) 27 | }) 28 | }) 29 | -------------------------------------------------------------------------------- /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/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/ui/src/icons/svg/eye-open.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pkg/ui/src/utils/open-window.js: -------------------------------------------------------------------------------- 1 | /** 2 | *Created by PanJiaChen on 16/11/29. 3 | * @param {Sting} url 4 | * @param {Sting} title 5 | * @param {Number} w 6 | * @param {Number} h 7 | */ 8 | export default function openWindow(url, title, w, h) { 9 | // Fixes dual-screen position Most browsers Firefox 10 | const dualScreenLeft = window.screenLeft !== undefined ? window.screenLeft : screen.left 11 | const dualScreenTop = window.screenTop !== undefined ? window.screenTop : screen.top 12 | 13 | const width = window.innerWidth ? window.innerWidth : document.documentElement.clientWidth ? document.documentElement.clientWidth : screen.width 14 | const height = window.innerHeight ? window.innerHeight : document.documentElement.clientHeight ? document.documentElement.clientHeight : screen.height 15 | 16 | const left = ((width / 2) - (w / 2)) + dualScreenLeft 17 | const top = ((height / 2) - (h / 2)) + dualScreenTop 18 | const newWindow = window.open(url, title, 'toolbar=no, location=no, directories=no, status=no, menubar=no, scrollbars=no, resizable=yes, copyhistory=no, width=' + w + ', height=' + h + ', top=' + top + ', left=' + left) 19 | 20 | // Puts focus on the newWindow 21 | if (window.focus) { 22 | newWindow.focus() 23 | } 24 | } 25 | 26 | -------------------------------------------------------------------------------- /pkg/ui/src/views/guide/steps.js: -------------------------------------------------------------------------------- 1 | const steps = [ 2 | { 3 | element: '#hamburger-container', 4 | popover: { 5 | title: 'Hamburger', 6 | description: 'Open && Close sidebar', 7 | position: 'bottom' 8 | } 9 | }, 10 | { 11 | element: '#breadcrumb-container', 12 | popover: { 13 | title: 'Breadcrumb', 14 | description: 'Indicate the current page location', 15 | position: 'bottom' 16 | } 17 | }, 18 | { 19 | element: '#header-search', 20 | popover: { 21 | title: 'Page Search', 22 | description: 'Page search, quick navigation', 23 | position: 'left' 24 | } 25 | }, 26 | { 27 | element: '#screenfull', 28 | popover: { 29 | title: 'Screenfull', 30 | description: 'Set the page into fullscreen', 31 | position: 'left' 32 | } 33 | }, 34 | { 35 | element: '#size-select', 36 | popover: { 37 | title: 'Switch Size', 38 | description: 'Switch the system size', 39 | position: 'left' 40 | } 41 | }, 42 | { 43 | element: '#tags-view-container', 44 | popover: { 45 | title: 'Tags view', 46 | description: 'The history of the page you visited', 47 | position: 'bottom' 48 | }, 49 | padding: 0 50 | } 51 | ] 52 | 53 | export default steps 54 | -------------------------------------------------------------------------------- /scripts/version.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" 4 | 5 | BUILD_GIT_REVISION=$(git rev-parse HEAD 2> /dev/null) 6 | if [[ $? == 0 ]]; then 7 | git diff-index --quiet HEAD 8 | if [[ $? != 0 ]]; then 9 | BUILD_GIT_REVISION=${BUILD_GIT_REVISION}"-dirty" 10 | fi 11 | else 12 | BUILD_GIT_REVISION=unknown 13 | fi 14 | 15 | # Check for local changes 16 | git diff-index --quiet HEAD -- 17 | if [[ $? == 0 ]]; 18 | then 19 | tree_status="Clean" 20 | else 21 | tree_status="Modified" 22 | fi 23 | 24 | # XXX This needs to be updated to accomodate tags added after building, rather than prior to builds 25 | RELEASE_TAG=$(git describe --match '[0-9]*\.[0-9]*\.[0-9]*' --exact-match 2> /dev/null || echo "") 26 | 27 | # security wanted VERSION='unknown' 28 | VERSION="${BUILD_GIT_REVISION}" 29 | if [[ -n "${RELEASE_TAG}" ]]; then 30 | VERSION="${RELEASE_TAG}" 31 | elif [[ -n ${ISTIO_VERSION} ]]; then 32 | VERSION="${ISTIO_VERSION}" 33 | fi 34 | 35 | # used by pkg/version 36 | echo buildVersion "${VERSION}" 37 | echo buildGitRevision "${BUILD_GIT_REVISION}" 38 | echo buildUser "$(whoami)" 39 | echo buildHost "$(hostname -f)" 40 | echo buildStatus "${tree_status}" 41 | echo buildTime "$(date +%Y-%m-%d--%T)" 42 | -------------------------------------------------------------------------------- /pkg/ui/tests/unit/utils/parseTime.spec.js: -------------------------------------------------------------------------------- 1 | import { parseTime } from '@/utils/index.js' 2 | 3 | describe('Utils:parseTime', () => { 4 | const d = new Date('2018-07-13 17:54:01') // "2018-07-13 17:54:01" 5 | it('timestamp', () => { 6 | expect(parseTime(d)).toBe('2018-07-13 17:54:01') 7 | }) 8 | 9 | it('timestamp string', () => { 10 | expect(parseTime((d + ''))).toBe('2018-07-13 17:54:01') 11 | }) 12 | 13 | it('ten digits timestamp', () => { 14 | expect(parseTime((d / 1000).toFixed(0))).toBe('2018-07-13 17:54:01') 15 | }) 16 | it('new Date', () => { 17 | expect(parseTime(new Date(d))).toBe('2018-07-13 17:54:01') 18 | }) 19 | it('format', () => { 20 | expect(parseTime(d, '{y}-{m}-{d} {h}:{i}')).toBe('2018-07-13 17:54') 21 | expect(parseTime(d, '{y}-{m}-{d}')).toBe('2018-07-13') 22 | expect(parseTime(d, '{y}/{m}/{d} {h}-{i}')).toBe('2018/07/13 17-54') 23 | }) 24 | it('get the day of the week', () => { 25 | expect(parseTime(d, '{a}')).toBe('五') // 星期五 26 | }) 27 | it('get the day of the week', () => { 28 | expect(parseTime(+d + 1000 * 60 * 60 * 24 * 2, '{a}')).toBe('日') // 星期日 29 | }) 30 | it('empty argument', () => { 31 | expect(parseTime()).toBeNull() 32 | }) 33 | 34 | it('null', () => { 35 | expect(parseTime(null)).toBeNull() 36 | }) 37 | }) 38 | -------------------------------------------------------------------------------- /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/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/ui/src/components/Screenfull/index.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 50 | 51 | 61 | -------------------------------------------------------------------------------- /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/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/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/h5/src/components/common/TopMenu.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 19 | 20 | 50 | -------------------------------------------------------------------------------- /pkg/ui/src/directive/el-table/adaptive.js: -------------------------------------------------------------------------------- 1 | import { addResizeListener, removeResizeListener } from 'element-ui/src/utils/resize-event' 2 | 3 | /** 4 | * How to use 5 | * ... 6 | * el-table height is must be set 7 | * bottomOffset: 30(default) // The height of the table from the bottom of the page. 8 | */ 9 | 10 | const doResize = (el, binding, vnode) => { 11 | const { componentInstance: $table } = vnode 12 | 13 | const { value } = binding 14 | 15 | if (!$table.height) { 16 | throw new Error(`el-$table must set the height. Such as height='100px'`) 17 | } 18 | const bottomOffset = (value && value.bottomOffset) || 30 19 | 20 | if (!$table) return 21 | 22 | const height = window.innerHeight - el.getBoundingClientRect().top - bottomOffset 23 | $table.layout.setHeight(height) 24 | $table.doLayout() 25 | } 26 | 27 | export default { 28 | bind(el, binding, vnode) { 29 | el.resizeListener = () => { 30 | doResize(el, binding, vnode) 31 | } 32 | // parameter 1 is must be "Element" type 33 | addResizeListener(window.document.body, el.resizeListener) 34 | }, 35 | inserted(el, binding, vnode) { 36 | doResize(el, binding, vnode) 37 | }, 38 | unbind(el) { 39 | removeResizeListener(window.document.body, el.resizeListener) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /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/ui/src/components/Hamburger/index.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 32 | 33 | 45 | -------------------------------------------------------------------------------- /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/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/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/src/assets/user.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /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/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/ui/src/icons/svg/404.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pkg/ui/plop-templates/view/prompt.js: -------------------------------------------------------------------------------- 1 | const { notEmpty } = require('../utils.js') 2 | 3 | module.exports = { 4 | description: 'generate a view', 5 | prompts: [{ 6 | type: 'input', 7 | name: 'name', 8 | message: 'view name please', 9 | validate: notEmpty('name') 10 | }, 11 | { 12 | type: 'checkbox', 13 | name: 'blocks', 14 | message: 'Blocks:', 15 | choices: [{ 16 | name: '