├── frontend
├── .env.development
├── .prettierrc
├── .gitignore
├── src
│ ├── index.css
│ ├── assets
│ │ └── logo.png
│ ├── components
│ │ ├── Logo.vue
│ │ ├── Loading.vue
│ │ ├── Header.vue
│ │ └── HelloWorld.vue
│ ├── main.js
│ ├── router.js
│ ├── App.vue
│ ├── api
│ │ └── index.js
│ └── views
│ │ ├── index.vue
│ │ └── login.vue
├── public
│ └── favicon.ico
├── postcss.config.js
├── jsconfig.json
├── .editorconfig
├── tailwind.config.js
├── index.html
├── package.json
└── vite.config.js
├── backend
├── static
│ ├── favicon.ico
│ ├── activity.json.gz
│ ├── assets
│ │ ├── logo.03d6d6da.png
│ │ ├── element-icons.de5eb258.ttf
│ │ ├── element-icons.9c88a535.woff
│ │ ├── index.34a494f0.js
│ │ └── index.fa13dcb4.css
│ ├── index.html
│ └── activity.json
├── ecosystem.config.js
├── .env.example
├── package.json
├── .gitignore
├── app.js
├── ql.js
├── utils
│ └── USER_AGENT.js
├── user.js
└── sendNotify.js
├── .github
└── workflows
│ └── vue.yml
├── .gitignore
└── README.md
/frontend/.env.development:
--------------------------------------------------------------------------------
1 | VITE_API_BASE_URL=http://localhost:5701/api
2 |
--------------------------------------------------------------------------------
/frontend/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "semi": false,
3 | "singleQuote": true
4 | }
5 |
--------------------------------------------------------------------------------
/frontend/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .DS_Store
3 | dist
4 | dist-ssr
5 | *.local
6 |
--------------------------------------------------------------------------------
/frontend/src/index.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
--------------------------------------------------------------------------------
/backend/static/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/licklly/kingrom_ninja/HEAD/backend/static/favicon.ico
--------------------------------------------------------------------------------
/frontend/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/licklly/kingrom_ninja/HEAD/frontend/public/favicon.ico
--------------------------------------------------------------------------------
/frontend/src/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/licklly/kingrom_ninja/HEAD/frontend/src/assets/logo.png
--------------------------------------------------------------------------------
/backend/static/activity.json.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/licklly/kingrom_ninja/HEAD/backend/static/activity.json.gz
--------------------------------------------------------------------------------
/frontend/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | }
7 |
--------------------------------------------------------------------------------
/backend/static/assets/logo.03d6d6da.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/licklly/kingrom_ninja/HEAD/backend/static/assets/logo.03d6d6da.png
--------------------------------------------------------------------------------
/backend/static/assets/element-icons.de5eb258.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/licklly/kingrom_ninja/HEAD/backend/static/assets/element-icons.de5eb258.ttf
--------------------------------------------------------------------------------
/backend/ecosystem.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | apps: [
3 | {
4 | name: 'ninja',
5 | script: './app.js',
6 | },
7 | ],
8 | };
9 |
--------------------------------------------------------------------------------
/backend/static/assets/element-icons.9c88a535.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/licklly/kingrom_ninja/HEAD/backend/static/assets/element-icons.9c88a535.woff
--------------------------------------------------------------------------------
/frontend/jsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "baseUrl": ".",
4 | "paths": {
5 | "@/*": ["src/*"]
6 | }
7 | },
8 | "exclude": ["node_modules", "dist"]
9 | }
10 |
--------------------------------------------------------------------------------
/frontend/.editorconfig:
--------------------------------------------------------------------------------
1 | # editorconfig.org
2 | root = true
3 |
4 | [*]
5 | indent_style = space
6 | indent_size = 2
7 | end_of_line = lf
8 | charset = utf-8
9 | trim_trailing_whitespace = true
10 | insert_final_newline = true
11 |
12 | [*.md]
13 | trim_trailing_whitespace = false
14 |
--------------------------------------------------------------------------------
/frontend/src/components/Logo.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
16 |
--------------------------------------------------------------------------------
/frontend/tailwind.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | purge: ['./index.html', './src/**/*.{vue,js,ts,jsx,tsx}'],
3 | important: '#app',
4 | darkMode: false, // or 'media' or 'class'
5 | theme: {
6 | extend: {},
7 | },
8 | variants: {
9 | extend: {},
10 | },
11 | plugins: [],
12 | }
13 |
--------------------------------------------------------------------------------
/frontend/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Ninja
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/backend/.env.example:
--------------------------------------------------------------------------------
1 | # 是否允许添加账号 不允许添加时则只允许已有账号登录
2 | ALLOW_ADD=true
3 |
4 | #允许添加账号的最大数量
5 | ALLOW_NUM=45
6 |
7 | #是否显示扫码,默认不显示
8 | #SHOW_QR=true
9 |
10 | #是否显示WSCK录入,默认不显示
11 | #SHOW_WSCK=false
12 |
13 | #是否显示CK登录,默认不显示
14 | SHOW_CK=true
15 |
16 | # 是否允许添加WSCK账号 不允许添加时则只允许已有账号登录
17 | ALLOW_WSCK_ADD=true
18 |
19 | #允许添加WSCK账号的最大数量
20 | ALLOW_WSCK_NUM=45
21 |
22 | # Ninja 运行端口
23 | NINJA_PORT=5701
24 |
25 | # Ninja 是否发送通知
26 | NINJA_NOTIFY=true
27 |
28 | # user-agent
29 | # NINJA_UA=""
30 |
--------------------------------------------------------------------------------
/backend/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "1.1.1",
3 | "scripts": {
4 | "dev": "nodemon app.js"
5 | },
6 | "engines": {
7 | "node": ">=14"
8 | },
9 | "dependencies": {
10 | "@koa/cors": "^3.1.0",
11 | "@koa/router": "^10.1.0",
12 | "axios": "^0.21.1",
13 | "dotenv": "^10.0.0",
14 | "got": "^11.8.2",
15 | "koa": "^2.13.1",
16 | "koa-body": "^4.2.0",
17 | "koa-log4": "^2.3.2",
18 | "koa-static": "^5.0.0",
19 | "nodemon": "^2.0.12",
20 | "qrcode": "^1.4.4"
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/frontend/src/components/Loading.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
16 |
17 |
18 |
19 |
20 |
21 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/backend/static/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Ninja
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/frontend/src/main.js:
--------------------------------------------------------------------------------
1 | import { ElButton, ElInput, ElMessage } from 'element-plus'
2 | import 'element-plus/lib/theme-chalk/base.css'
3 | import { createApp } from 'vue'
4 | import App from './App.vue'
5 | import './index.css'
6 | import router from './router'
7 |
8 | const components = [ElButton, ElInput, ElMessage]
9 | const plugins = [ElMessage]
10 |
11 | const app = createApp(App)
12 |
13 | components.forEach((component) => {
14 | app.component(component.name, component)
15 | })
16 |
17 | plugins.forEach((plugin) => {
18 | app.use(plugin)
19 | })
20 |
21 | app.use(router)
22 | app.mount('#app')
23 |
--------------------------------------------------------------------------------
/frontend/src/router.js:
--------------------------------------------------------------------------------
1 | import Index from '@/views/index.vue'
2 | import Login from '@/views/login.vue'
3 | import { createRouter, createWebHashHistory } from 'vue-router'
4 |
5 | const routes = [
6 | { path: '/', component: Index },
7 | { path: '/login', component: Login },
8 | ]
9 |
10 | const router = createRouter({
11 | history: createWebHashHistory(),
12 | routes,
13 | })
14 |
15 | // router.beforeEach((to, from, next) => {
16 | // if (!localStorage.getItem('eid') && to.path !== '/login')
17 | // next({ path: '/login' })
18 | // else next()
19 | // })
20 |
21 | export default router
22 |
--------------------------------------------------------------------------------
/frontend/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "0.0.0",
3 | "scripts": {
4 | "dev": "vite",
5 | "build": "vite build",
6 | "serve": "vite preview"
7 | },
8 | "dependencies": {
9 | "element-plus": "^1.0.2-beta.62",
10 | "ky": "^0.28.5",
11 | "vue": "^3.1.5",
12 | "vue-router": "^4.0.10"
13 | },
14 | "devDependencies": {
15 | "@vitejs/plugin-vue": "^1.2.5",
16 | "@vue/compiler-sfc": "^3.1.5",
17 | "autoprefixer": "^10.3.1",
18 | "postcss": "^8.3.6",
19 | "tailwindcss": "^2.2.7",
20 | "vite": "^2.4.3",
21 | "vite-plugin-async-catch": "^0.1.7",
22 | "vite-plugin-style-import": "^1.0.1"
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/frontend/src/components/Header.vue:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
12 |
20 |
21 |
29 |
--------------------------------------------------------------------------------
/frontend/src/components/HelloWorld.vue:
--------------------------------------------------------------------------------
1 |
2 | {{ msg }}
3 |
4 |
5 |
6 | Vite Documentation
7 |
8 | |
9 | Vue 3 Documentation
10 |
11 |
12 | count is: {{ state.count }}
13 |
14 | Edit
15 | components/HelloWorld.vue to test hot module replacement.
16 |
17 |
18 |
19 |
28 |
29 |
34 |
--------------------------------------------------------------------------------
/.github/workflows/vue.yml:
--------------------------------------------------------------------------------
1 | name: BuildAndCommit
2 |
3 | env:
4 | TZ: Asia/Shanghai
5 |
6 | on:
7 | workflow_dispatch:
8 |
9 | jobs:
10 | main:
11 | runs-on: ubuntu-latest
12 | if: github.event.repository.owner.id == github.event.sender.id
13 | steps:
14 | - name: Checkout
15 | uses: actions/checkout@v2
16 |
17 | - name: Install and Build
18 | run: |
19 | cd backend/static
20 | rm -f index.html
21 | cd assets
22 | shopt -s extglob
23 | rm -f !(*.png)
24 | cd ..
25 | cd ..
26 | cd ..
27 | cd frontend
28 | npm install
29 | npm run build
30 |
31 | - name: Commit changes
32 | uses: EndBug/add-and-commit@v4
33 | with:
34 | message: "GitHub Actions Auto Commit"
35 | add: "backend/static"
36 | env:
37 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
38 |
--------------------------------------------------------------------------------
/frontend/vite.config.js:
--------------------------------------------------------------------------------
1 | import vue from '@vitejs/plugin-vue'
2 | import path from 'path'
3 | import { defineConfig } from 'vite'
4 | import AsyncCatch from 'vite-plugin-async-catch'
5 | import styleImport from 'vite-plugin-style-import'
6 |
7 | // https://vitejs.dev/config/
8 | export default defineConfig({
9 | plugins: [
10 | vue(),
11 | AsyncCatch({ catchCode: `console.error(e)` }),
12 | styleImport({
13 | libs: [
14 | {
15 | libraryName: 'element-plus',
16 | esModule: true,
17 | ensureStyleFile: true,
18 | resolveStyle: (name) => {
19 | return `element-plus/lib/theme-chalk/${name}.css`
20 | },
21 | resolveComponent: (name) => {
22 | return `element-plus/lib/${name}`
23 | },
24 | },
25 | ],
26 | }),
27 | ],
28 | resolve: {
29 | alias: {
30 | '@': path.resolve(__dirname, 'src'),
31 | },
32 | },
33 | build: {
34 | outDir: '../backend/static',
35 | },
36 | })
37 |
--------------------------------------------------------------------------------
/backend/static/activity.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "name": "玩一玩(可找到大多数活动)",
4 | "address": "京东 APP 首页-频道-边玩边赚",
5 | "href": "https://funearth.m.jd.com/babelDiy/Zeus/3BB1rymVZUo4XmicATEUSDUgHZND/index.html"
6 | },
7 | {
8 | "name": "宠汪汪",
9 | "address": "京东APP-首页/玩一玩/我的-宠汪汪"
10 | },
11 | {
12 | "name": "东东萌宠",
13 | "address": "京东APP-首页/玩一玩/我的-东东萌宠"
14 | },
15 | {
16 | "name": "东东农场",
17 | "address": "京东APP-首页/玩一玩/我的-东东农场"
18 | },
19 | {
20 | "name": "东东工厂",
21 | "address": "京东APP-首页/玩一玩/我的-东东工厂"
22 | },
23 | {
24 | "name": "东东超市",
25 | "address": "京东APP-首页/玩一玩/我的-东东超市"
26 | },
27 | {
28 | "name": "领现金",
29 | "address": "京东APP-首页/玩一玩/我的-领现金"
30 | },
31 | {
32 | "name": "东东健康社区",
33 | "address": "京东APP-首页/玩一玩/我的-东东健康社区"
34 | },
35 | {
36 | "name": "京喜农场",
37 | "address": "京喜APP-我的-京喜农场"
38 | },
39 | {
40 | "name": "京喜牧场",
41 | "address": "京喜APP-我的-京喜牧场"
42 | },
43 | {
44 | "name": "京喜工厂",
45 | "address": "京喜APP-我的-京喜工厂"
46 | },
47 | {
48 | "name": "京喜财富岛",
49 | "address": "京喜APP-我的-京喜财富岛"
50 | },
51 | {
52 | "name": "京东极速版红包",
53 | "address": "京东极速版APP-我的-红包"
54 | }
55 | ]
56 |
--------------------------------------------------------------------------------
/frontend/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
15 |
16 |
--------------------------------------------------------------------------------
/frontend/src/api/index.js:
--------------------------------------------------------------------------------
1 | import ky from 'ky'
2 |
3 | const VITE_API_BASE_URL = import.meta.env.VITE_API_BASE_URL || '/api'
4 |
5 | const api = ky.create({ prefixUrl: VITE_API_BASE_URL, retry: { limit: 0 } })
6 |
7 | export function getInfoAPI() {
8 | return api.get('info').json()
9 | }
10 |
11 | export function CKLoginAPI(body) {
12 | return api.post('cklogin', { json: body }).json()
13 | }
14 |
15 | export function getQrcodeAPI() {
16 | return api.get('qrcode').json()
17 | }
18 |
19 | export function checkLoginAPI(body) {
20 | return api.post('check', { json: body }).json()
21 | }
22 |
23 | export function getUserInfoAPI(eid) {
24 | const searchParams = new URLSearchParams()
25 | searchParams.set('eid', eid)
26 | return api.get('userinfo', { searchParams: searchParams }).json()
27 | }
28 |
29 | export function delAccountAPI(body) {
30 | return api.post('delaccount', { json: body }).json()
31 | }
32 |
33 | export function remarkupdateAPI(body) {
34 | return api.post('update/remark', { json: body }).json()
35 | }
36 |
37 | export function WSCKLoginAPI(body) {
38 | return api.post('WSCKLogin', { json: body }).json()
39 | }
40 |
41 | export function getWSCKUserinfoAPI(eid) {
42 | const searchParams = new URLSearchParams()
43 | searchParams.set('wseid', wseid)
44 | return api.get('WSCKUserinfo', { searchParams: searchParams }).json()
45 | }
46 |
47 | export function WSCKDelaccountAPI(body) {
48 | return api.post('WSCKDelaccount', { json: body }).json()
49 | }
50 |
51 | export function remarkupdateWSCKAPI(body) {
52 | return api.post('updateWSCK/remark', { json: body }).json()
53 | }
54 |
--------------------------------------------------------------------------------
/backend/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | # Created by https://www.toptal.com/developers/gitignore/api/node,windows,macos,linux
3 | # Edit at https://www.toptal.com/developers/gitignore?templates=node,windows,macos,linux
4 |
5 | ### Linux ###
6 | *~
7 |
8 | # temporary files which can be created if a process still has a handle open of a deleted file
9 | .fuse_hidden*
10 |
11 | # KDE directory preferences
12 | .directory
13 |
14 | # Linux trash folder which might appear on any partition or disk
15 | .Trash-*
16 |
17 | # .nfs files are created when an open file is removed but is still being accessed
18 | .nfs*
19 |
20 | ### macOS ###
21 | # General
22 | .DS_Store
23 | .AppleDouble
24 | .LSOverride
25 |
26 | # Icon must end with two \r
27 | Icon
28 |
29 |
30 | # Thumbnails
31 | ._*
32 |
33 | # Files that might appear in the root of a volume
34 | .DocumentRevisions-V100
35 | .fseventsd
36 | .Spotlight-V100
37 | .TemporaryItems
38 | .Trashes
39 | .VolumeIcon.icns
40 | .com.apple.timemachine.donotpresent
41 |
42 | # Directories potentially created on remote AFP share
43 | .AppleDB
44 | .AppleDesktop
45 | Network Trash Folder
46 | Temporary Items
47 | .apdisk
48 |
49 | ### Node ###
50 | # Logs
51 | logs
52 | *.log
53 | npm-debug.log*
54 | yarn-debug.log*
55 | yarn-error.log*
56 | lerna-debug.log*
57 | .pnpm-debug.log*
58 |
59 | # Diagnostic reports (https://nodejs.org/api/report.html)
60 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
61 |
62 | # Runtime data
63 | pids
64 | *.pid
65 | *.seed
66 | *.pid.lock
67 |
68 | # Directory for instrumented libs generated by jscoverage/JSCover
69 | lib-cov
70 |
71 | # Coverage directory used by tools like istanbul
72 | coverage
73 | *.lcov
74 |
75 | # nyc test coverage
76 | .nyc_output
77 |
78 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
79 | .grunt
80 |
81 | # Bower dependency directory (https://bower.io/)
82 | bower_components
83 |
84 | # node-waf configuration
85 | .lock-wscript
86 |
87 | # Compiled binary addons (https://nodejs.org/api/addons.html)
88 | build/Release
89 |
90 | # Dependency directories
91 | node_modules/
92 | jspm_packages/
93 |
94 | # Snowpack dependency directory (https://snowpack.dev/)
95 | web_modules/
96 |
97 | # TypeScript cache
98 | *.tsbuildinfo
99 |
100 | # Optional npm cache directory
101 | .npm
102 |
103 | # Optional eslint cache
104 | .eslintcache
105 |
106 | # Microbundle cache
107 | .rpt2_cache/
108 | .rts2_cache_cjs/
109 | .rts2_cache_es/
110 | .rts2_cache_umd/
111 |
112 | # Optional REPL history
113 | .node_repl_history
114 |
115 | # Output of 'npm pack'
116 | *.tgz
117 |
118 | # Yarn Integrity file
119 | .yarn-integrity
120 |
121 | # dotenv environment variables file
122 | .env
123 | .env.test
124 | .env.production
125 |
126 | # parcel-bundler cache (https://parceljs.org/)
127 | .cache
128 | .parcel-cache
129 |
130 | # Next.js build output
131 | .next
132 | out
133 |
134 | # Nuxt.js build / generate output
135 | .nuxt
136 | dist
137 |
138 | # Gatsby files
139 | .cache/
140 | # Comment in the public line in if your project uses Gatsby and not Next.js
141 | # https://nextjs.org/blog/next-9-1#public-directory-support
142 | # public
143 |
144 | # vuepress build output
145 | .vuepress/dist
146 |
147 | # Serverless directories
148 | .serverless/
149 |
150 | # FuseBox cache
151 | .fusebox/
152 |
153 | # DynamoDB Local files
154 | .dynamodb/
155 |
156 | # TernJS port file
157 | .tern-port
158 |
159 | # Stores VSCode versions used for testing VSCode extensions
160 | .vscode-test
161 |
162 | # yarn v2
163 | .yarn/cache
164 | .yarn/unplugged
165 | .yarn/build-state.yml
166 | .yarn/install-state.gz
167 | .pnp.*
168 |
169 | ### Windows ###
170 | # Windows thumbnail cache files
171 | Thumbs.db
172 | Thumbs.db:encryptable
173 | ehthumbs.db
174 | ehthumbs_vista.db
175 |
176 | # Dump file
177 | *.stackdump
178 |
179 | # Folder config file
180 | [Dd]esktop.ini
181 |
182 | # Recycle Bin used on file shares
183 | $RECYCLE.BIN/
184 |
185 | # Windows Installer files
186 | *.cab
187 | *.msi
188 | *.msix
189 | *.msm
190 | *.msp
191 |
192 | # Windows shortcuts
193 | *.lnk
194 |
195 | # End of https://www.toptal.com/developers/gitignore/api/node,windows,macos,linux
196 |
197 | ql
198 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | # Created by https://www.toptal.com/developers/gitignore/api/node,windows,macos,linux
3 | # Edit at https://www.toptal.com/developers/gitignore?templates=node,windows,macos,linux
4 |
5 | ### Linux ###
6 | *~
7 |
8 | # temporary files which can be created if a process still has a handle open of a deleted file
9 | .fuse_hidden*
10 |
11 | # KDE directory preferences
12 | .directory
13 |
14 | # Linux trash folder which might appear on any partition or disk
15 | .Trash-*
16 |
17 | # .nfs files are created when an open file is removed but is still being accessed
18 | .nfs*
19 |
20 | ### macOS ###
21 | # General
22 | .DS_Store
23 | .AppleDouble
24 | .LSOverride
25 |
26 | # Icon must end with two \r
27 | Icon
28 |
29 |
30 | # Thumbnails
31 | ._*
32 |
33 | # Files that might appear in the root of a volume
34 | .DocumentRevisions-V100
35 | .fseventsd
36 | .Spotlight-V100
37 | .TemporaryItems
38 | .Trashes
39 | .VolumeIcon.icns
40 | .com.apple.timemachine.donotpresent
41 |
42 | # Directories potentially created on remote AFP share
43 | .AppleDB
44 | .AppleDesktop
45 | Network Trash Folder
46 | Temporary Items
47 | .apdisk
48 |
49 | ### Node ###
50 | # Logs
51 | logs
52 | *.log
53 | npm-debug.log*
54 | yarn-debug.log*
55 | yarn-error.log*
56 | lerna-debug.log*
57 | .pnpm-debug.log*
58 |
59 | # Diagnostic reports (https://nodejs.org/api/report.html)
60 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
61 |
62 | # Runtime data
63 | pids
64 | *.pid
65 | *.seed
66 | *.pid.lock
67 |
68 | # Directory for instrumented libs generated by jscoverage/JSCover
69 | lib-cov
70 |
71 | # Coverage directory used by tools like istanbul
72 | coverage
73 | *.lcov
74 |
75 | # nyc test coverage
76 | .nyc_output
77 |
78 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
79 | .grunt
80 |
81 | # Bower dependency directory (https://bower.io/)
82 | bower_components
83 |
84 | # node-waf configuration
85 | .lock-wscript
86 |
87 | # Compiled binary addons (https://nodejs.org/api/addons.html)
88 | build/Release
89 |
90 | # Dependency directories
91 | node_modules/
92 | jspm_packages/
93 |
94 | # Snowpack dependency directory (https://snowpack.dev/)
95 | web_modules/
96 |
97 | # TypeScript cache
98 | *.tsbuildinfo
99 |
100 | # Optional npm cache directory
101 | .npm
102 |
103 | # Optional eslint cache
104 | .eslintcache
105 |
106 | # Microbundle cache
107 | .rpt2_cache/
108 | .rts2_cache_cjs/
109 | .rts2_cache_es/
110 | .rts2_cache_umd/
111 |
112 | # Optional REPL history
113 | .node_repl_history
114 |
115 | # Output of 'npm pack'
116 | *.tgz
117 |
118 | # Yarn Integrity file
119 | .yarn-integrity
120 |
121 | # dotenv environment variables file
122 | .env
123 | .env.test
124 | .env.production
125 |
126 | # parcel-bundler cache (https://parceljs.org/)
127 | .cache
128 | .parcel-cache
129 |
130 | # Next.js build output
131 | .next
132 | out
133 |
134 | # Nuxt.js build / generate output
135 | .nuxt
136 | dist
137 |
138 | # Gatsby files
139 | .cache/
140 | # Comment in the public line in if your project uses Gatsby and not Next.js
141 | # https://nextjs.org/blog/next-9-1#public-directory-support
142 | # public
143 |
144 | # vuepress build output
145 | .vuepress/dist
146 |
147 | # Serverless directories
148 | .serverless/
149 |
150 | # FuseBox cache
151 | .fusebox/
152 |
153 | # DynamoDB Local files
154 | .dynamodb/
155 |
156 | # TernJS port file
157 | .tern-port
158 |
159 | # Stores VSCode versions used for testing VSCode extensions
160 | .vscode-test
161 |
162 | # yarn v2
163 | .yarn/cache
164 | .yarn/unplugged
165 | .yarn/build-state.yml
166 | .yarn/install-state.gz
167 | .pnp.*
168 |
169 | ### Windows ###
170 | # Windows thumbnail cache files
171 | Thumbs.db
172 | Thumbs.db:encryptable
173 | ehthumbs.db
174 | ehthumbs_vista.db
175 |
176 | # Dump file
177 | *.stackdump
178 |
179 | # Folder config file
180 | [Dd]esktop.ini
181 |
182 | # Recycle Bin used on file shares
183 | $RECYCLE.BIN/
184 |
185 | # Windows Installer files
186 | *.cab
187 | *.msi
188 | *.msix
189 | *.msm
190 | *.msp
191 |
192 | # Windows shortcuts
193 | *.lnk
194 |
195 | # End of https://www.toptal.com/developers/gitignore/api/node,windows,macos,linux
196 |
197 | ql
198 |
199 | node_modules
200 | .DS_Store
201 | dist
202 | dist-ssr
203 | *.local
204 |
--------------------------------------------------------------------------------
/backend/app.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const Koa = require('koa');
4 | const cors = require('@koa/cors');
5 | const Router = require('@koa/router');
6 | const body = require('koa-body');
7 | const serve = require('koa-static');
8 | const User = require('./user');
9 | const packageJson = require('./package.json');
10 |
11 | // Create express instance
12 | const app = new Koa();
13 | const router = new Router();
14 |
15 | const handler = async (ctx, next) => {
16 | try {
17 | await next();
18 | if (ctx.body?.data.message) {
19 | ctx.body.message = ctx.body.data.message;
20 | ctx.body.data.message = undefined;
21 | }
22 | } catch (err) {
23 | console.log(err);
24 | ctx.status = err.statusCode || err.status || 500;
25 | ctx.body = {
26 | code: err.status || err.statusCode || 500,
27 | message: err.message,
28 | };
29 | }
30 | };
31 |
32 | app.use(serve('static'));
33 | app.use(cors());
34 | app.use(handler);
35 | app.use(router.routes()).use(router.allowedMethods());
36 |
37 | router.get('/api/status', (ctx) => {
38 | ctx.body = {
39 | code: 200,
40 | data: {
41 | version: packageJson.version,
42 | },
43 | message: 'Ninja is already.',
44 | };
45 | });
46 |
47 | router.get('/api/info', async (ctx) => {
48 | const data = await User.getPoolInfo();
49 | debugger
50 | ctx.body = { data };
51 | });
52 |
53 | router.get('/api/qrcode', async (ctx) => {
54 | const user = new User({});
55 | await user.getQRConfig();
56 | ctx.body = {
57 | data: {
58 | token: user.token,
59 | okl_token: user.okl_token,
60 | cookies: user.cookies,
61 | QRCode: user.QRCode,
62 | ua: user.ua,
63 | },
64 | };
65 | });
66 |
67 | router.post('/api/check', body(), async (ctx) => {
68 | const body = ctx.request.body;
69 | const user = new User(body);
70 | const data = await user.checkQRLogin();
71 | ctx.body = { data };
72 | });
73 |
74 | router.post('/api/cklogin', body(), async (ctx) => {
75 | const body = ctx.request.body;
76 | const user = new User(body);
77 | const data = await user.CKLogin();
78 | ctx.body = { data };
79 | });
80 |
81 | router.get('/api/userinfo', async (ctx) => {
82 | const query = ctx.query;
83 | const eid = query.eid;
84 | const user = new User({ eid });
85 | const data = await user.getUserInfoByEid();
86 | ctx.body = { data };
87 | });
88 |
89 | router.post('/api/delaccount', body(), async (ctx) => {
90 | const body = ctx.request.body;
91 | const eid = body.eid;
92 | const user = new User({ eid });
93 | const data = await user.delUserByEid();
94 | ctx.body = { data };
95 | });
96 |
97 | router.post('/api/update/remark', body(), async (ctx) => {
98 | const body = ctx.request.body;
99 | const eid = body.eid;
100 | const remark = body.remark;
101 | const user = new User({ eid, remark });
102 | const data = await user.updateRemark();
103 | ctx.body = { data };
104 | });
105 |
106 | router.get('/api/users', async (ctx) => {
107 | if (ctx.host.startsWith('localhost')) {
108 | const data = await User.getUsers();
109 | ctx.body = { data };
110 | } else {
111 | ctx.body = {
112 | code: 401,
113 | message: '该接口仅能通过 localhost 访问',
114 | };
115 | }
116 | });
117 |
118 | ///////////////////////////////////////////////
119 |
120 | router.post('/api/WSCKLogin', body(), async (ctx) => {
121 | const body = ctx.request.body;
122 | const user = new User(body);
123 | const data = await user.WSCKLogin();
124 | ctx.body = { data };
125 | });
126 |
127 | router.get('/api/WSCKUserinfo', async (ctx) => {
128 | const query = ctx.query;
129 | const wseid = query.wseid;
130 | const user = new User({ wseid });
131 | const data = await user.getWSCKUserInfoByEid();
132 | ctx.body = { data };
133 | });
134 |
135 | router.post('/api/WSCKDelaccount', body(), async (ctx) => {
136 | const body = ctx.request.body;
137 | const wseid = body.wseid;
138 | const user = new User({ wseid });
139 | const data = await user.delWSCKUserByEid();
140 | ctx.body = { data };
141 | });
142 |
143 | router.post('/api/updateWSCK/remark', body(), async (ctx) => {
144 | const body = ctx.request.body;
145 | const wseid = body.wseid;
146 | const remark = body.remark;
147 | const user = new User({ wseid, remark });
148 | const data = await user.updateWSCKRemark();
149 | ctx.body = { data };
150 | });
151 |
152 | ///////////////////////////////////////////////
153 |
154 | const port = process.env.NINJA_PORT || 5701;
155 | console.log('Start Ninja success! listening port: ' + port);
156 | app.listen(port);
157 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Ninja
2 |
3 | 支持CK注册,登录和删除,支持WSKEY录入和删除,登录成功进入个人中心,可修改备注。默认登录CK才可提交WSCK,主页提交WSCK容易乱,不建议。
4 |
5 | 基本功能已完善,鸽几天,有问题先仔细看此README。
6 |
7 | ## 致谢
8 |
9 | 感谢Ninja原作者:@MoonBegonia
10 |
11 | 仓库地址:https://github.com/MoonBegonia/ninja
12 |
13 | 感谢WSCK功能原作者:@huiyi9420
14 |
15 | 仓库地址:https://github.com/huiyi9420/ninja
16 |
17 | ## 新
18 |
19 | 当前:增加备用接口(针对某些半黑号)
20 |
21 | 新特性:支持Github Action前端编译并自动替换文件。Fork之后:Action->BuildAndCommit->Run workflow->Run workflow即可。
22 |
23 | ## 说明
24 |
25 | Ninja 仅供学习参考使用,请于下载后的 24 小时内删除,本人不对使用过程中出现的任何问题负责,包括但不限于 `数据丢失` `数据泄露`。
26 |
27 | Ninja 仅支持 qinglong 2.8.2+
28 |
29 | [TG 频道](https://t.me/joinchat/sHKuteb_lfdjNmZl)
30 |
31 | ## 特性
32 |
33 | - [x] 局域网扫码,跳转登录添加/更新 cookie
34 | - [x] 添加/更新 cookie 后发送通知
35 | - [x] 扫码发送通知可关闭
36 | - [x] 默认备注为账号
37 | - [x] 可修改备注
38 | - [x] wskey有效性检测
39 | - [x] 登录界面展示自定义标语
40 | - [x] Github Action自动编译
41 | - [x] WSKEY录入
42 |
43 | ## 文档
44 |
45 | ### 容器内
46 |
47 | 1. 容器映射 5701 端口,ninja 目录至宿主机
48 |
49 | 例(docker-compose):
50 |
51 | ```diff
52 | version: "3"
53 | services:
54 | qinglong:
55 | image: whyour/qinglong:latest
56 | container_name: qinglong
57 | restart: unless-stopped
58 | tty: true
59 | ports:
60 | - 5700:5700
61 | + - 5701:5701
62 | environment:
63 | - ENABLE_HANGUP=true
64 | - ENABLE_WEB_PANEL=true
65 | volumes:
66 | - ./config:/ql/config
67 | - ./log:/ql/log
68 | - ./db:/ql/db
69 | - ./repo:/ql/repo
70 | - ./raw:/ql/raw
71 | - ./scripts:/ql/scripts
72 | - ./jbot:/ql/jbot
73 | + - ./ninja:/ql/ninja
74 | ```
75 |
76 | 例(docker-run):
77 |
78 | ```diff
79 | docker run -dit \
80 | -v $PWD/ql/config:/ql/config \
81 | -v $PWD/ql/log:/ql/log \
82 | -v $PWD/ql/db:/ql/db \
83 | -v $PWD/ql/repo:/ql/repo \
84 | -v $PWD/ql/raw:/ql/raw \
85 | -v $PWD/ql/scripts:/ql/scripts \
86 | -v $PWD/ql/jbot:/ql/jbot \
87 | + -v $PWD/ql/ninja:/ql/ninja \
88 | -p 5700:5700 \
89 | + -p 5701:5701 \
90 | --name qinglong \
91 | --hostname qinglong \
92 | --restart unless-stopped \
93 | whyour/qinglong:latest
94 | ```
95 |
96 | 2. 进容器内执行以下命令
97 |
98 | **进容器内执行以下命令**
99 |
100 | ```bash
101 | git clone https://github.com/KingRan/kingrom_ninja.git /ql/ninja
102 | cd /ql/ninja/backend
103 | pnpm install
104 | cp .env.example .env # 如有需要, 修改.env
105 | pm2 start
106 | cp sendNotify.js /ql/scripts/sendNotify.js
107 | ```
108 |
109 | 3. 将以下内容粘贴到 `extra.sh`(重启后自动更新并启动 Ninja)
110 |
111 | ```bash
112 | cd /ql/ninja/backend
113 | git checkout .
114 | git pull
115 | pnpm install
116 | pm2 start
117 | cp sendNotify.js /ql/scripts/sendNotify.js
118 | ```
119 |
120 | ### 容器外
121 |
122 | 此种方式需要宿主机安装 `node` `pnpm` 等环境,不做过多介绍。
123 |
124 | 使用此种方法无法跟随青龙一起启动,**无法发送扫码通知**,请知悉。
125 |
126 | ```bash
127 | git clone https://github.com/KingRan/kingrom_ninja.git
128 | cd ninja/backend
129 | pnpm install
130 | # 复制 sendNotify.js 到容器内 scripts 目录,`qinglong` 为容器名
131 | sudo docker cp sendNotify.js qinglong:/ql/scripts/sendNotify.js
132 | cp .env.example .env
133 | # 修改env文件
134 | vi .env
135 | node app.js
136 | ```
137 |
138 | 在 `.env` 文件中添加以下内容:
139 |
140 | ```bash
141 | QL_DIR=qinglong 容器的本地路径
142 | QL_URL=http://localhost:5700
143 | ```
144 |
145 | `node app.js` 想要在后台运行可以使用 `&` `nohup` `screen` 等命令。
146 |
147 | ### Ninja 环境变量
148 |
149 | 目前支持的环境变量有:
150 |
151 | - `SHOW_QR`:是否显示扫码卡片,默认不显示
152 | - `SHOW_WSCK`:是否显示WSCK录入,默认不显示
153 | - `SHOW_CK`:是否显示CK登录,默认不显示
154 | - `ALLOW_WSCK_ADD`:是否允许添加WSCK账号 不允许添加时则只允许已有账号登录
155 | - `ALLOW_WSCK_NUM`:允许添加WSCK账号的最大数量
156 | - `ALLOW_ADD`: 是否允许添加账号 不允许添加时则只允许已有账号登录(默认 `true`)
157 | - `ALLOW_NUM`: 允许添加账号的最大数量(默认 `45`)
158 | - `NINJA_PORT`: Ninja 运行端口(默认 `5701`)
159 | - `NINJA_NOTIFY`: 是否开启通知功能(默认 `true`)
160 | - `NINJA_UA`: 自定义 UA,默认为随机
161 | -
162 |
163 | 配置方式:
164 |
165 | ```bash
166 | cd /ql/ninja/backend
167 | cp .env.example .env
168 | vi .env
169 | pm2 start
170 | ```
171 |
172 | **修改完成后需要 `pm2 start` 重启生效 !!!**
173 |
174 | ### SendNotify 环境变量
175 |
176 | **此环境变量在青龙中配置!!!**
177 |
178 | - `NOTIFY_SKIP_LIST`: 通知黑名单,使用 `&` 分隔,例如 `东东乐园&东东萌宠`;
179 |
180 | ### Ninja 自定义
181 |
182 | (未完成)自定义推送二维码:将 `push.jpg` 文件添加到 `/ql/ninja/backend/static/` 目录下刷新网页即可。
183 |
184 | 自定义常见活动:修改 `/ql/backend/static/activity.json` 即可
185 |
186 | ## 注意事项
187 |
188 | - 重启后务必执行一次 `ql extra` 保证 Ninja 配置成功。
189 |
190 | - 更新 Ninja 只需要在**容器**中 `ninja/backend` 目录执行 `git pull` 然后 `pm2 start`
191 |
192 | - Qinglong 需要在登录状态(`auth.json` 中有 token)
193 |
194 | ## 如何更新Ninja
195 |
196 | ```bash
197 | cd /ql/ninja
198 | git checkout .
199 | git pull
200 | cd backend
201 | pm2 start
202 | ```
203 |
204 | ## 如何删除Ninja
205 |
206 | ```bash
207 | cd /ql/ninja
208 | pm2 delete ninja
209 | rm -rf *
210 | rm -r ./.*
211 | ```
212 |
--------------------------------------------------------------------------------
/backend/ql.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const got = require('got');
4 | require('dotenv').config();
5 | const { readFile } = require('fs/promises');
6 | const path = require('path');
7 |
8 | const qlDir = process.env.QL_DIR || '/ql';
9 | const authFile = path.join(qlDir, 'config/auth.json');
10 |
11 | const api = got.extend({
12 | prefixUrl: process.env.QL_URL || 'http://localhost:5600',
13 | retry: { limit: 0 },
14 | });
15 |
16 | async function getToken() {
17 | const authConfig = JSON.parse(await readFile(authFile));
18 | return authConfig.token;
19 | }
20 |
21 | module.exports.getEnvs = async () => {
22 | const token = await getToken();
23 | const body = await api({
24 | url: 'api/envs',
25 | searchParams: {
26 | searchValue: 'JD_COOKIE',
27 | t: Date.now(),
28 | },
29 | headers: {
30 | Accept: 'application/json',
31 | authorization: `Bearer ${token}`,
32 | },
33 | }).json();
34 | return body.data;
35 | };
36 |
37 | module.exports.getEnvsCount = async () => {
38 | const data = await this.getEnvs();
39 | return data.length;
40 | };
41 |
42 | module.exports.addEnv = async (cookie, remarks) => {
43 | const token = await getToken();
44 | const body = await api({
45 | method: 'post',
46 | url: 'api/envs',
47 | params: { t: Date.now() },
48 | json: [{
49 | name: 'JD_COOKIE',
50 | value: cookie,
51 | remarks,
52 | }],
53 | headers: {
54 | Accept: 'application/json',
55 | authorization: `Bearer ${token}`,
56 | 'Content-Type': 'application/json;charset=UTF-8',
57 | },
58 | }).json();
59 | return body;
60 | };
61 |
62 | module.exports.updateEnv = async (cookie, eid, remarks) => {
63 | const token = await getToken();
64 | const body = await api({
65 | method: 'put',
66 | url: 'api/envs',
67 | params: { t: Date.now() },
68 | json: {
69 | name: 'JD_COOKIE',
70 | value: cookie,
71 | _id: eid,
72 | remarks,
73 | },
74 | headers: {
75 | Accept: 'application/json',
76 | authorization: `Bearer ${token}`,
77 | 'Content-Type': 'application/json;charset=UTF-8',
78 | },
79 | }).json();
80 | return body;
81 | };
82 |
83 | module.exports.delEnv = async (eid) => {
84 | const token = await getToken();
85 | const body = await api({
86 | method: 'delete',
87 | url: 'api/envs',
88 | params: { t: Date.now() },
89 | body: JSON.stringify([eid]),
90 | headers: {
91 | Accept: 'application/json',
92 | authorization: `Bearer ${token}`,
93 | 'Content-Type': 'application/json;charset=UTF-8',
94 | },
95 | }).json();
96 | return body;
97 | };
98 |
99 | //////////////////////////////////////////////////
100 | // wskey
101 | module.exports.getWSCKEnvs = async () => {
102 | const token = await getToken();
103 | const body = await api({
104 | url: 'api/envs',
105 | searchParams: {
106 | searchValue: 'JD_WSCK',
107 | t: Date.now(),
108 | },
109 | headers: {
110 | Accept: 'application/json',
111 | authorization: `Bearer ${token}`,
112 | },
113 | }).json();
114 | return body.data;
115 | };
116 |
117 | module.exports.getWSCKEnvsCount = async () => {
118 | const data = await this.getWSCKEnvs();
119 | return data.length;
120 | };
121 |
122 | module.exports.addWSCKEnv = async (jdwsck, remarks) => {
123 | const token = await getToken();
124 | const body = await api({
125 | method: 'post',
126 | url: 'api/envs',
127 | params: { t: Date.now() },
128 | json: [{
129 | name: 'JD_WSCK',
130 | value: jdwsck,
131 | remarks,
132 | }],
133 | headers: {
134 | Accept: 'application/json',
135 | authorization: `Bearer ${token}`,
136 | 'Content-Type': 'application/json;charset=UTF-8',
137 | },
138 | }).json();
139 | return body;
140 | };
141 |
142 | module.exports.updateWSCKEnv = async (jdwsck, wseid, remarks) => {
143 | const token = await getToken();
144 | const body = await api({
145 | method: 'put',
146 | url: 'api/envs',
147 | params: { t: Date.now() },
148 | json: {
149 | name: 'JD_WSCK',
150 | value: jdwsck,
151 | _id: wseid,
152 | remarks,
153 | },
154 | headers: {
155 | Accept: 'application/json',
156 | authorization: `Bearer ${token}`,
157 | 'Content-Type': 'application/json;charset=UTF-8',
158 | },
159 | }).json();
160 | return body;
161 | };
162 |
163 | module.exports.delWSCKEnv = async (wseid) => {
164 | const token = await getToken();
165 | const body = await api({
166 | method: 'delete',
167 | url: 'api/envs',
168 | params: { t: Date.now() },
169 | body: JSON.stringify([wseid]),
170 | headers: {
171 | Accept: 'application/json',
172 | authorization: `Bearer ${token}`,
173 | 'Content-Type': 'application/json;charset=UTF-8',
174 | },
175 | }).json();
176 | return body;
177 | };
178 |
179 | //////////////////////////////////////////////////
180 |
--------------------------------------------------------------------------------
/backend/utils/USER_AGENT.js:
--------------------------------------------------------------------------------
1 | const USER_AGENTS = [
2 | 'jdapp;android;10.0.2;10;network/wifi;Mozilla/5.0 (Linux; Android 10; ONEPLUS A5010 Build/QKQ1.191014.012; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/77.0.3865.120 MQQBrowser/6.2 TBS/045230 Mobile Safari/537.36',
3 | 'jdapp;iPhone;10.0.2;14.3;network/wifi;Mozilla/5.0 (iPhone; CPU iPhone OS 14_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148;supportJDSHWK/1',
4 | 'jdapp;android;10.0.2;9;network/wifi;Mozilla/5.0 (Linux; Android 9; Mi Note 3 Build/PKQ1.181007.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/66.0.3359.126 MQQBrowser/6.2 TBS/045131 Mobile Safari/537.36',
5 | 'jdapp;android;10.0.2;10;network/wifi;Mozilla/5.0 (Linux; Android 10; GM1910 Build/QKQ1.190716.003; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/77.0.3865.120 MQQBrowser/6.2 TBS/045230 Mobile Safari/537.36',
6 | 'jdapp;android;10.0.2;9;network/wifi;Mozilla/5.0 (Linux; Android 9; 16T Build/PKQ1.190616.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/66.0.3359.126 MQQBrowser/6.2 TBS/044942 Mobile Safari/537.36',
7 | 'jdapp;iPhone;10.0.2;13.6;network/wifi;Mozilla/5.0 (iPhone; CPU iPhone OS 13_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148;supportJDSHWK/1',
8 | 'jdapp;iPhone;10.0.2;13.6;network/wifi;Mozilla/5.0 (iPhone; CPU iPhone OS 13_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148;supportJDSHWK/1',
9 | 'jdapp;iPhone;10.0.2;13.5;network/wifi;Mozilla/5.0 (iPhone; CPU iPhone OS 13_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148;supportJDSHWK/1',
10 | 'jdapp;iPhone;10.0.2;14.1;network/wifi;Mozilla/5.0 (iPhone; CPU iPhone OS 14_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148;supportJDSHWK/1',
11 | 'jdapp;iPhone;10.0.2;13.3;network/wifi;Mozilla/5.0 (iPhone; CPU iPhone OS 13_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148;supportJDSHWK/1',
12 | 'jdapp;iPhone;10.0.2;13.7;network/wifi;Mozilla/5.0 (iPhone; CPU iPhone OS 13_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148;supportJDSHWK/1',
13 | 'jdapp;iPhone;10.0.2;14.1;network/wifi;Mozilla/5.0 (iPhone; CPU iPhone OS 14_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148;supportJDSHWK/1',
14 | 'jdapp;iPhone;10.0.2;13.3;network/wifi;Mozilla/5.0 (iPhone; CPU iPhone OS 13_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148;supportJDSHWK/1',
15 | 'jdapp;iPhone;10.0.2;13.4;network/wifi;Mozilla/5.0 (iPhone; CPU iPhone OS 13_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148;supportJDSHWK/1',
16 | 'jdapp;iPhone;10.0.2;14.3;network/wifi;Mozilla/5.0 (iPhone; CPU iPhone OS 14_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148;supportJDSHWK/1',
17 | 'jdapp;android;10.0.2;9;network/wifi;Mozilla/5.0 (Linux; Android 9; MI 6 Build/PKQ1.190118.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/66.0.3359.126 MQQBrowser/6.2 TBS/044942 Mobile Safari/537.36',
18 | 'jdapp;android;10.0.2;11;network/wifi;Mozilla/5.0 (Linux; Android 11; Redmi K30 5G Build/RKQ1.200826.002; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/77.0.3865.120 MQQBrowser/6.2 TBS/045511 Mobile Safari/537.36',
19 | 'jdapp;iPhone;10.0.2;11.4;network/wifi;Mozilla/5.0 (iPhone; CPU iPhone OS 11_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15F79',
20 | 'jdapp;android;10.0.2;10;;network/wifi;Mozilla/5.0 (Linux; Android 10; M2006J10C Build/QP1A.190711.020; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/77.0.3865.120 MQQBrowser/6.2 TBS/045230 Mobile Safari/537.36',
21 | 'jdapp;android;10.0.2;10;network/wifi;Mozilla/5.0 (Linux; Android 10; M2006J10C Build/QP1A.190711.020; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/77.0.3865.120 MQQBrowser/6.2 TBS/045230 Mobile Safari/537.36',
22 | 'jdapp;android;10.0.2;10;network/wifi;Mozilla/5.0 (Linux; Android 10; ONEPLUS A6000 Build/QKQ1.190716.003; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/77.0.3865.120 MQQBrowser/6.2 TBS/045224 Mobile Safari/537.36',
23 | 'jdapp;android;10.0.2;9;network/wifi;Mozilla/5.0 (Linux; Android 9; MHA-AL00 Build/HUAWEIMHA-AL00; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/66.0.3359.126 MQQBrowser/6.2 TBS/044942 Mobile Safari/537.36',
24 | 'jdapp;android;10.0.2;8.1.0;network/wifi;Mozilla/5.0 (Linux; Android 8.1.0; 16 X Build/OPM1.171019.026; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/66.0.3359.126 MQQBrowser/6.2 TBS/044942 Mobile Safari/537.36',
25 | 'jdapp;android;10.0.2;8.0.0;network/wifi;Mozilla/5.0 (Linux; Android 8.0.0; HTC U-3w Build/OPR6.170623.013; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/66.0.3359.126 MQQBrowser/6.2 TBS/044942 Mobile Safari/537.36',
26 | 'jdapp;iPhone;10.0.2;14.0.1;network/wifi;Mozilla/5.0 (iPhone; CPU iPhone OS 14_0_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148;supportJDSHWK/1',
27 | 'jdapp;android;10.0.2;10;network/wifi;Mozilla/5.0 (Linux; Android 10; LYA-AL00 Build/HUAWEILYA-AL00L; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/77.0.3865.120 MQQBrowser/6.2 TBS/045230 Mobile Safari/537.36',
28 | 'jdapp;iPhone;10.0.2;14.2;network/wifi;Mozilla/5.0 (iPhone; CPU iPhone OS 14_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148;supportJDSHWK/1',
29 | 'jdapp;iPhone;10.0.2;14.3;network/wifi;Mozilla/5.0 (iPhone; CPU iPhone OS 14_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148;supportJDSHWK/1',
30 | 'jdapp;iPhone;10.0.2;14.2;network/wifi;Mozilla/5.0 (iPhone; CPU iPhone OS 14_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148;supportJDSHWK/1',
31 | 'jdapp;android;10.0.2;8.1.0;network/wifi;Mozilla/5.0 (Linux; Android 8.1.0; MI 8 Build/OPM1.171019.026; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/66.0.3359.126 MQQBrowser/6.2 TBS/045131 Mobile Safari/537.36',
32 | 'jdapp;android;10.0.2;10;network/wifi;Mozilla/5.0 (Linux; Android 10; Redmi K20 Pro Premium Edition Build/QKQ1.190825.002; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/77.0.3865.120 MQQBrowser/6.2 TBS/045227 Mobile Safari/537.36',
33 | 'jdapp;iPhone;10.0.2;14.3;network/wifi;Mozilla/5.0 (iPhone; CPU iPhone OS 14_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148;supportJDSHWK/1',
34 | 'jdapp;iPhone;10.0.2;14.3;network/wifi;Mozilla/5.0 (iPhone; CPU iPhone OS 14_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148;supportJDSHWK/1',
35 | 'jdapp;android;10.0.2;11;network/wifi;Mozilla/5.0 (Linux; Android 11; Redmi K20 Pro Premium Edition Build/RKQ1.200826.002; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/77.0.3865.120 MQQBrowser/6.2 TBS/045513 Mobile Safari/537.36',
36 | 'jdapp;android;10.0.2;10;network/wifi;Mozilla/5.0 (Linux; Android 10; MI 8 Build/QKQ1.190828.002; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/77.0.3865.120 MQQBrowser/6.2 TBS/045227 Mobile Safari/537.36',
37 | 'jdapp;iPhone;10.0.2;14.1;network/wifi;Mozilla/5.0 (iPhone; CPU iPhone OS 14_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148;supportJDSHWK/1',
38 | ];
39 | /**
40 | * 生成随机数字
41 | * @param {number} min 最小值(包含)
42 | * @param {number} max 最大值(不包含)
43 | */
44 | function randomNumber(min = 0, max = 100) {
45 | return Math.min(Math.floor(min + Math.random() * (max - min)), max);
46 | }
47 |
48 | const RANDOM_UA = USER_AGENTS[randomNumber(0, USER_AGENTS.length)];
49 |
50 | const GET_RANDOM_TIME_UA = () => {
51 | return 'Mozilla/5.0 (iPhone; CPU iPhone OS 13_3_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 SP-engine/2.14.0 main%2F1.0 baiduboxapp/11.18.0.16 (Baidu; P2 13.3.1) NABar/0.0';
52 | };
53 |
54 | module.exports = {
55 | RANDOM_UA,
56 | GET_RANDOM_TIME_UA,
57 | };
58 |
--------------------------------------------------------------------------------
/frontend/src/views/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
昵称:{{ nickName }}
9 |
更新时间:{{ timestamp }}
10 |
11 |
15 |
16 |
17 |
18 |
31 |
32 |
33 |
34 |
38 |
39 |
40 |
41 |
44 |
45 |
46 |
47 |
50 |
51 |
52 |
53 |
57 |
58 |
59 |
64 | {{ item.name }}:
65 | {{ item.address }}
66 | 直达链接
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
281 |
282 |
296 |
--------------------------------------------------------------------------------
/frontend/src/views/login.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
10 |
为了您的财产安全请关闭免密支付以及打开支付验密(京东-设置-支付设置-支付验密设置)。
11 |
建议京东账户绑定微信以保证提现能到账。
12 |
由于京东异地登录限制,扫码获取cookie只有2小时有效期,因此暂时关闭扫码功能,现需手动抓取Cookie。
13 |
且有效期不长,平均3-5天,因此需要及时更新。
14 |
安全起见,WSCK可以在CK登录后录入,期限半永久。
15 |
16 |
17 |
18 |
19 |
20 |
29 |
30 |
31 | 扫描二维码登录
34 | 跳转到京东 App 登录
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
59 |
60 |
61 | 录入
62 |
63 |
64 |
65 |
66 |
67 |
80 |
81 |
82 | 登录
83 |
84 |
85 |
86 |
87 |
88 |
89 |
263 |
264 |
278 |
--------------------------------------------------------------------------------
/backend/static/assets/index.34a494f0.js:
--------------------------------------------------------------------------------
1 | var e=Object.defineProperty,a=Object.defineProperties,s=Object.getOwnPropertyDescriptors,t=Object.getOwnPropertySymbols,o=Object.prototype.hasOwnProperty,c=Object.prototype.propertyIsEnumerable,r=(a,s,t)=>s in a?e(a,s,{enumerable:!0,configurable:!0,writable:!0,value:t}):a[s]=t,n=(e,a)=>{for(var s in a||(a={}))o.call(a,s)&&r(e,s,a[s]);if(t)for(var s of t(a))c.call(a,s)&&r(e,s,a[s]);return e},i=(e,t)=>a(e,s(t));"undefined"!=typeof require&&require;import{p as l,a as d,o as p,c as m,r as u,b as k,d as y,F as f,k as v,u as w,e as g,f as b,g as h,t as x,h as C,w as j,i as S,_ as P,j as _,l as K,m as A,n as W,q as I,s as L,v as O,x as V}from"./vendor.4eb73c88.js";!function(){const e=document.createElement("link").relList;if(!(e&&e.supports&&e.supports("modulepreload"))){for(const e of document.querySelectorAll('link[rel="modulepreload"]'))a(e);new MutationObserver((e=>{for(const s of e)if("childList"===s.type)for(const e of s.addedNodes)"LINK"===e.tagName&&"modulepreload"===e.rel&&a(e)})).observe(document,{childList:!0,subtree:!0})}function a(e){if(e.ep)return;e.ep=!0;const a=function(e){const a={};return e.integrity&&(a.integrity=e.integrity),e.referrerpolicy&&(a.referrerPolicy=e.referrerpolicy),"use-credentials"===e.crossorigin?a.credentials="include":"anonymous"===e.crossorigin?a.credentials="omit":a.credentials="same-origin",a}(e);fetch(e.href,a)}}();l("data-v-4b23e37a"),d();const N={},Q={class:"NinjaLogo",src:"/assets/logo.03d6d6da.png",alt:"logo"};N.render=function(e,a){return p(),m("img",Q)};const U={components:{Logo:N}};l("data-v-1f23ce5f");const q={class:"header"},D={class:"header-wrapper"},R={class:"flex items-center"},z=k("p",{class:"pl-3 select-none"},"Ninja",-1);d(),U.render=function(e,a,s,t,o,c){const r=u("Logo");return p(),m("div",q,[k("div",D,[k("div",R,[y(r,{class:"h-10 w-10"}),z])])])},U.__scopeId="data-v-1f23ce5f";const T={class:"main"},E={setup:e=>(e,a)=>{const s=u("router-view");return p(),m(f,null,[y(U),k("div",T,[y(s)])],64)}};const J=v.create({prefixUrl:"/api",retry:{limit:0}});function $(e){return J.post("WSCKLogin",{json:e}).json()}const Z={setup(){const e=w();g();let a=b({remark:"",jdwsck:void 0,nickName:void 0,timestamp:void 0});const s=async()=>{try{const e=localStorage.getItem("eid"),s=localStorage.getItem("wseid");if(!e&&!s)return void t();if(e){const s=await function(e){const a=new URLSearchParams;return a.set("eid",e),J.get("userinfo",{searchParams:a}).json()}(e);if(!s)return P.error("获取用户CK信息失败,请重重新登录"),void t();a.nickName=s.data.nickName,a.timestamp=new Date(s.data.timestamp).toLocaleString()}if(s){const e=await getWSCKUserinfoAPI(s);if(!e)return P.error("获取用户WSCK信息失败,请重重新登录"),void t();a.nickName=e.data.nickName,a.timestamp=new Date(e.data.timestamp).toLocaleString()}}catch(e){console.error(e)}};h(s);const t=()=>{localStorage.removeItem("eid"),localStorage.removeItem("wseid"),e.push("/login")};return i(n({},x(a)),{activity:[{name:"玩一玩(可找到大多数活动)",address:"京东 APP 首页-频道-边玩边赚",href:"https://funearth.m.jd.com/babelDiy/Zeus/3BB1rymVZUo4XmicATEUSDUgHZND/index.html"},{name:"宠汪汪",address:"京东APP-首页/玩一玩/我的-宠汪汪"},{name:"东东萌宠",address:"京东APP-首页/玩一玩/我的-东东萌宠"},{name:"东东农场",address:"京东APP-首页/玩一玩/我的-东东农场"},{name:"东东工厂",address:"京东APP-首页/玩一玩/我的-东东工厂"},{name:"东东超市",address:"京东APP-首页/玩一玩/我的-东东超市"},{name:"领现金",address:"京东APP-首页/玩一玩/我的-领现金"},{name:"东东健康社区",address:"京东APP-首页/玩一玩/我的-东东健康社区"},{name:"京喜农场",address:"京喜APP-我的-京喜农场"},{name:"京喜牧场",address:"京喜APP-我的-京喜牧场"},{name:"京喜工厂",address:"京喜APP-我的-京喜工厂"},{name:"京喜财富岛",address:"京喜APP-我的-京喜财富岛"},{name:"京东极速版红包",address:"京东极速版APP-我的-红包"}],getInfo:s,logout:t,delAccount:async()=>{try{const e=localStorage.getItem("eid"),a=await function(e){return J.post("delaccount",{json:e}).json()}({eid:e});200!==a.code?P.error(a.message):(P.success(a.message),setTimeout((()=>{t()}),1e3))}catch(e){console.error(e)}},changeremark:async()=>{try{const s=localStorage.getItem("eid"),t=localStorage.getItem("wseid"),o=a.remark;if(s){const e=await function(e){return J.post("update/remark",{json:e}).json()}({eid:s,remark:o});200!==e.code?P.success(e.message):P.error(e.message)}if(t){const a=await(e={wseid:t,remark:o},J.post("updateWSCK/remark",{json:e}).json());200!==a.code?P.success(a.message):P.error(a.message)}}catch(s){console.error(s)}var e},WSCKLogin:async()=>{try{const e=a.jdwsck.match(/wskey=(.*?);/)&&a.jdwsck.match(/wskey=(.*?);/)[1],s=a.jdwsck.match(/pin=(.*?);/)&&a.jdwsck.match(/pin=(.*?);/)[1];if(e&&s){const a=await $({wskey:e,pin:s});a.data.wseid?(localStorage.setItem("wseid",a.data.wseid),P.success(a.message)):P.error(a.message||"wskey 解析失败,请检查后重试!")}else P.error("wskey 解析失败,请检查后重试!")}catch(e){console.error(e)}},delWSCKAccount:async()=>{try{const e=localStorage.getItem("wseid"),a=await function(e){return J.post("WSCKDelaccount",{json:e}).json()}({wseid:e});200!==a.code?P.error(a.message):(P.success(a.message),setTimeout((()=>{t()}),1e3))}catch(e){console.error(e)}},openUrlWithJD:e=>{const a=encodeURIComponent(`{"category":"jump","des":"m","action":"to","url":"${e}"}`);window.location.href=`openapp.jdmobile://virtual?params=${a}`,console.log(window.location.href)}})}};l("data-v-71afa157");const B={class:"content"},G={class:"card"},H=k("div",{class:"card-header"},[k("p",{class:"card-title"},"个人中心")],-1),F={class:"card-body"},M={class:"card-footer"},X=A("退出登录"),Y=A("删除CK"),ee={class:"card"},ae=_('',1),se={class:"card-body text-center"},te={class:"card-footer"},oe=A("重新录入"),ce=A("删除WSCK"),re={class:"card"},ne=k("div",{class:"card-header"},[k("p",{class:"card-title"},"修改备注(CK和WSCK同步)")],-1),ie={class:"card-body text-center"},le={class:"card-footer"},de=A("修改"),pe={class:"card"},me=k("div",{class:"card-header"},[k("p",{class:"card-title"},"常见活动位置"),k("span",{class:"card-subtitle"},"下面是一些常见活动的位置")],-1),ue={class:"card-body"},ke={class:"pr-2"},ye=["onClick"];d(),Z.render=function(e,a,s,t,o,c){const r=u("el-button"),n=u("el-input");return p(),m("div",B,[k("div",G,[H,k("div",F,[k("p",null,"昵称:"+C(e.nickName),1),k("p",null,"更新时间:"+C(e.timestamp),1)]),k("div",M,[y(r,{size:"small",auto:"",onClick:t.logout},{default:j((()=>[X])),_:1},8,["onClick"]),y(r,{type:"danger",size:"small",auto:"",onClick:t.delAccount},{default:j((()=>[Y])),_:1},8,["onClick"])])]),k("div",ee,[ae,k("div",se,[y(n,{modelValue:e.jdwsck,"onUpdate:modelValue":a[0]||(a[0]=a=>e.jdwsck=a),placeholder:"pin=xxxxxx;wskey=xxxxxxxxxx;",size:"small",clearable:"",class:"my-4 w-full"},null,8,["modelValue"])]),k("div",te,[y(r,{type:"success",size:"small",auto:"",onClick:t.WSCKLogin},{default:j((()=>[oe])),_:1},8,["onClick"]),y(r,{type:"danger",size:"small",auto:"",onClick:t.delWSCKAccount},{default:j((()=>[ce])),_:1},8,["onClick"])])]),k("div",re,[ne,k("div",ie,[y(n,{modelValue:e.remark,"onUpdate:modelValue":a[1]||(a[1]=a=>e.remark=a),size:"small",clearable:"",class:"my-4 w-full"},null,8,["modelValue"])]),k("div",le,[y(r,{type:"success",size:"small",auto:"",onClick:t.changeremark},{default:j((()=>[de])),_:1},8,["onClick"])])]),k("div",pe,[me,k("div",ue,[k("ul",null,[(p(!0),m(f,null,S(t.activity,((e,a)=>(p(),m("li",{key:a,class:"leading-normal"},[k("span",null,C(e.name)+":",1),k("span",ke,C(e.address),1),e.href?(p(),m("a",{key:0,class:"text-blue-400",href:"#",onClick:a=>t.openUrlWithJD(e.href)},"直达链接",8,ye)):K("",!0)])))),128))])])])])},Z.__scopeId="data-v-71afa157";const fe={setup(){const e=w();g();let a=b({marginCount:0,allowAdd:!0,cookie:"",QRCode:void 0,qrCodeVisibility:!1,token:void 0,okl_token:void 0,cookies:void 0,timer:void 0,waitLogin:!1,marginWSCKCount:0,allowWSCKAdd:!0,jdwsck:void 0,showQR:!1,showWSCK:!1,showCK:!0});const s=async()=>{try{const e=(await J.get("info").json()).data;a.marginCount=e.marginCount,a.allowAdd=e.allowAdd,a.marginWSCKCount=e.marginWSCKCount,a.allowWSCKAdd=e.allowWSCKAdd,a.showQR=e.showQR,a.showWSCK=e.showWSCK,a.showCK=e.showCK}catch(e){console.error(e)}},t=async()=>{if(this.showQR)try{const e=await J.get("qrcode").json();a.token=e.data.token,a.okl_token=e.data.okl_token,a.cookies=e.data.cookies,a.QRCode=e.data.QRCode,a.QRCode&&(a.waitLogin=!0,clearInterval(a.timer),a.timer=setInterval(o,3e3))}catch(e){console.error(e),P.error("生成二维码失败!请重试或放弃")}else P.warning("扫码已禁用请手动抓包")},o=async()=>{try{const s=await function(e){return J.post("check",{json:e}).json()}({token:a.token,okl_token:a.okl_token,cookies:a.cookies});switch(null==s?void 0:s.data.errcode){case 0:localStorage.setItem("eid",s.data.eid),P.success(s.message),clearInterval(a.timer),e.push("/");break;case 176:break;default:P.error(s.message),a.waitLogin=!1,clearInterval(a.timer)}}catch(s){clearInterval(a.timer),a.waitLogin=!1}};return h((()=>{s(),t()})),i(n({},x(a)),{getInfo:s,getQrcode:t,showQrcode:async()=>{a.qrCodeVisibility=!0},ckeckLogin:o,jumpLogin:async()=>{const e=`openapp.jdmobile://virtual/ad?params={"category":"jump","des":"ThirdPartyLogin","action":"to","onekeylogin":"return","url":"https://plogin.m.jd.com/cgi-bin/m/tmauth?appid=300&client_type=m&token=${a.token}","authlogin_returnurl":"weixin://","browserlogin_fromurl":"${window.location.host}"}`;window.location.href=e},CKLogin:async()=>{try{const s=a.cookie.match(/pt_key=(.*?);/)&&a.cookie.match(/pt_key=(.*?);/)[1],t=a.cookie.match(/pt_pin=(.*?);/)&&a.cookie.match(/pt_pin=(.*?);/)[1];if(s&&t){const a=await function(e){return J.post("cklogin",{json:e}).json()}({pt_key:s,pt_pin:t});a.data.eid?(localStorage.setItem("eid",a.data.eid),P.success(a.message),e.push("/")):P.error(a.message||"cookie 解析失败,请检查后重试!")}else P.error("cookie 解析失败,请检查后重试!")}catch(s){console.error(s)}},WSCKLogin:async()=>{try{const s=a.jdwsck.match(/wskey=(.*?);/)&&a.jdwsck.match(/wskey=(.*?);/)[1],t=a.jdwsck.match(/pin=(.*?);/)&&a.jdwsck.match(/pin=(.*?);/)[1];if(s&&t){const a=await $({wskey:s,pin:t});a.data.wseid?(localStorage.setItem("wseid",a.data.wseid),P.success(a.message),e.push("/")):P.error(a.message||"wskey 解析失败,请检查后重试!")}else P.error("wskey 解析失败,请检查后重试!")}catch(s){console.error(s)}}})}};l("data-v-7065ab79");const ve={class:"content"},we=_('为了您的财产安全请关闭免密支付以及打开支付验密(京东-设置-支付设置-支付验密设置)。
建议京东账户绑定微信以保证提现能到账。
由于京东异地登录限制,扫码获取cookie只有2小时有效期,因此暂时关闭扫码功能,现需手动抓取Cookie。
且有效期不长,平均3-5天,因此需要及时更新。
安全起见,WSCK可以在CK登录后录入,期限半永久。 ',1),ge={key:0,class:"card"},be={class:"card-header"},he={class:"flex items-center justify-between"},xe=k("p",{class:"card-title"},"扫码登录",-1),Ce={class:"ml-2 px-2 py-1 bg-gray-200 rounded-full font-normal text-xs"},je=k("span",{class:"card-subtitle"}," 请点击下方按钮登录,点击按钮后回到本网站查看是否登录成功,京东的升级提示不用管。 ",-1),Se={class:"card-body text-center"},Pe={key:0,class:"flex flex-col w-48 m-auto mt-4"},_e=A("扫描二维码登录"),Ke=A("跳转到京东 App 登录"),Ae=["src"],We=k("div",{class:"card-footer"},null,-1),Ie={key:1,class:"card"},Le={class:"card-header"},Oe={class:"flex items-center justify-between"},Ve=k("p",{class:"card-title"},"WSCK 录入",-1),Ne={class:"ml-2 px-2 py-1 bg-gray-200 rounded-full font-normal text-xs"},Qe=_('wskey有效期长达一年,请联系管理员确认使用(删不掉,慎用) 用户须手动提取pin和wskey,格式如:"pt_pin=xxxxxx;wskey=xxxxxxxxxx;"。
——IOS用户手机抓包APP 点击跳转安装
——在api.m.jd.com域名下找POST请求大概率能找到wskey。
wskey在录入后立马上线,系统会在指定时间检查wskey,有效则自动转换出cookie登录
cookie失效后,也会在系统设定的指定时间内自动转换出新的cookie,实现一次录入长期有效
请在下方输入您的 WSCK ',2),Ue={class:"card-body text-center"},qe=A("录入"),De=k("div",{class:"card-footet"},null,-1),Re={key:2,class:"card"},ze={class:"card-header"},Te={class:"flex items-center justify-between"},Ee=k("p",{class:"card-title"},"CK 登录",-1),Je={class:"ml-2 px-2 py-1 bg-gray-200 rounded-full font-normal text-xs"},$e=k("div",{class:"card-body text-base leading-6"},[k("p",null,[A("PC用户建议使用浏览器"),k("a",{style:{},href:"https://www.juan920.com/?s=cookie",target:"_blank",id:"kingrom"},"Cookie获取教程"),A("获取cookie并在下方填写。")]),k("p",null,[A("手机用户可以使用Alook浏览器登录"),k("a",{style:{},href:"https://m.jd.com/",target:"_blank",id:"jd"},"JD官网"),A(",并在菜单-工具箱-开发者工具-Cookies中获取(Android和iPhone通用)。")]),k("p",null,"另外也可以使用抓包工具(iPhone:Stream,Android:HttpCanary)抓取京东app的ck,要注意pt_key和pt_pin字段是以app_open开头的。"),k("p",null,"cookie直接填入输入框即可,Ninja会自动正则提取pt_key和pt_pin。")],-1),Ze=k("span",{class:"card-subtitle"}," 请在下方输入您的 cookie 登录。 ",-1),Be={class:"card-body text-center"},Ge=A("登录"),He=k("div",{class:"card-footet"},null,-1);d(),fe.render=function(e,a,s,t,o,c){const r=u("el-button"),n=u("el-input");return p(),m("div",ve,[we,e.showQR?(p(),m("div",ge,[k("div",be,[k("div",he,[xe,k("span",Ce,"余量:"+C(e.marginCount),1)]),je]),k("div",Se,[e.qrCodeVisibility?(p(),m("img",{key:1,src:e.QRCode,width:256,class:"m-auto"},null,8,Ae)):(p(),m("div",Pe,[y(r,{type:"primary",round:"",onClick:t.showQrcode},{default:j((()=>[_e])),_:1},8,["onClick"]),y(r,{class:"mt-4 ml-0",type:"primary",round:"",onClick:t.jumpLogin},{default:j((()=>[Ke])),_:1},8,["onClick"])]))]),We])):K("",!0),e.showWSCK?(p(),m("div",Ie,[k("div",Le,[k("div",Oe,[Ve,k("span",Ne,"余量:"+C(e.marginWSCKCount),1)]),Qe]),k("div",Ue,[y(n,{modelValue:e.jdwsck,"onUpdate:modelValue":a[0]||(a[0]=a=>e.jdwsck=a),placeholder:"pin=xxxxxx;wskey=xxxxxxxxxx;",size:"small",clearable:"",class:"my-4 w-full"},null,8,["modelValue"]),y(r,{type:"primary",size:"small",round:"",onClick:t.WSCKLogin},{default:j((()=>[qe])),_:1},8,["onClick"])]),De])):K("",!0),e.showCK?(p(),m("div",Re,[k("div",ze,[k("div",Te,[Ee,k("span",Je,"余量:"+C(e.marginCount),1)]),$e,Ze]),k("div",Be,[y(n,{modelValue:e.cookie,"onUpdate:modelValue":a[1]||(a[1]=a=>e.cookie=a),size:"small",clearable:"",class:"my-4 w-full"},null,8,["modelValue"]),y(r,{type:"primary",size:"small",round:"",onClick:t.CKLogin},{default:j((()=>[Ge])),_:1},8,["onClick"])]),He])):K("",!0)])},fe.__scopeId="data-v-7065ab79";const Fe=[{path:"/",component:Z},{path:"/login",component:fe}],Me=W({history:I(),routes:Fe}),Xe=[O,V,P],Ye=[P],ea=L(E);Xe.forEach((e=>{ea.component(e.name,e)})),Ye.forEach((e=>{ea.use(e)})),ea.use(Me),ea.mount("#app");
2 |
--------------------------------------------------------------------------------
/backend/user.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable camelcase */
2 | 'use strict';
3 |
4 | const got = require('got');
5 | const got1 = require('got');
6 | require('dotenv').config();
7 | const QRCode = require('qrcode');
8 | // 新增 , addWSCKEnv, delWSCKEnv, getWSCKEnvs, getWSCKEnvsCount, updateWSCKEnv
9 | const { addEnv, delEnv, getEnvs, getEnvsCount, updateEnv , addWSCKEnv, delWSCKEnv, getWSCKEnvs, getWSCKEnvsCount, updateWSCKEnv } = require('./ql');
10 | const path = require('path');
11 | const qlDir = process.env.QL_DIR || '/ql';
12 | const notifyFile = path.join(qlDir, 'shell/notify.sh');
13 | const { exec } = require('child_process');
14 | const { GET_RANDOM_TIME_UA } = require('./utils/USER_AGENT');
15 |
16 | const api = got.extend({
17 | retry: { limit: 0 },
18 | responseType: 'json',
19 | });
20 |
21 | module.exports = class User {
22 | ua;
23 | pt_key;
24 | pt_pin;
25 | pin;// 新增变量
26 | wskey;// 新增变量
27 | jdwsck;// 新增变量
28 | code;// 新增变量
29 | msg;// 新增变量
30 | cookie;
31 | eid;
32 | wseid
33 | timestamp;
34 | nickName;
35 | token;
36 | okl_token;
37 | cookies;
38 | QRCode;
39 | remark;
40 | #s_token;
41 | // 新增wskey构造入参
42 | constructor({ token, okl_token, cookies, pt_key, pt_pin, cookie, eid, wseid, remarks, remark, ua, pin, wskey, jdwsck}) {
43 | this.token = token;
44 | this.okl_token = okl_token;
45 | this.cookies = cookies;
46 | this.pt_key = pt_key;
47 | this.pt_pin = pt_pin;
48 | this.cookie = cookie;
49 | this.eid = eid;
50 | this.wseid = wseid;
51 | this.remark = remark;
52 | this.ua = ua;
53 |
54 | if (pt_key && pt_pin) {
55 | this.cookie = 'pt_key=' + this.pt_key + ';pt_pin=' + this.pt_pin + ';';
56 | }
57 |
58 | if (cookie) {
59 | this.pt_pin = cookie.match(/pt_pin=(.*?);/)[1];
60 | this.pt_key = cookie.match(/pt_key=(.*?);/)[1];
61 | }
62 |
63 | if (remarks) {
64 | this.remark = remarks.match(/remark=(.*?);/) && remarks.match(/remark=(.*?);/)[1];
65 | }
66 | /////////////////////////////////////////////////
67 | // 新增pin
68 | this.pin = pin;
69 | // 新增wskey
70 | this.wskey = wskey;
71 | // 新增 jdwsck
72 | this.jdwsck = jdwsck;
73 | // 新增如果wskey和pin不是空则产生jdwsck
74 | if (pin && wskey) {
75 | this.jdwsck = 'pin=' + this.pin + ';wskey=' + this.wskey + ';';
76 | }
77 |
78 | // 新增如果备注是空则默认取pt_pin作为备注
79 | if (this.jdwsck && this.remark === null || this.remark === '') {
80 | this.remark = this.pin;
81 | }
82 |
83 | // 新增如果nickName是空默认取pt_pin作为备注
84 | if (this.jdwsck && this.nickName === null || this.nickName === '') {
85 | this.nickName = this.pin;
86 | }
87 | /////////////////////////////////////////////////
88 | }
89 |
90 | async getQRConfig() {
91 | this.ua = this.ua || process.env.NINJA_UA || GET_RANDOM_TIME_UA();
92 | const taskUrl = `https://plogin.m.jd.com/cgi-bin/mm/new_login_entrance?lang=chs&appid=300&returnurl=https://wq.jd.com/passport/LoginRedirect?state=${Date.now()}&returnurl=https://home.m.jd.com/myJd/newhome.action?sceneval=2&ufc=&/myJd/home.action&source=wq_passport`;
93 | const response = await api({
94 | url: taskUrl,
95 | headers: {
96 | Connection: 'Keep-Alive',
97 | 'Content-Type': 'application/x-www-form-urlencoded',
98 | Accept: 'application/json, text/plain, */*',
99 | 'Accept-Language': 'zh-cn',
100 | Referer: taskUrl,
101 | 'User-Agent': this.ua,
102 | Host: 'plogin.m.jd.com',
103 | },
104 | });
105 | const headers = response.headers;
106 | const data = response.body;
107 | await this.#formatSetCookies(headers, data);
108 |
109 | if (!this.#s_token) {
110 | throw new Error('二维码创建失败!');
111 | }
112 |
113 | const nowTime = Date.now();
114 | // eslint-disable-next-line prettier/prettier
115 | const taskPostUrl = `https://plogin.m.jd.com/cgi-bin/m/tmauthreflogurl?s_token=${
116 | this.#s_token
117 | }&v=${nowTime}&remember=true`;
118 |
119 | const configRes = await api({
120 | method: 'post',
121 | url: taskPostUrl,
122 | body: `lang=chs&appid=300&source=wq_passport&returnurl=https://wqlogin2.jd.com/passport/LoginRedirect?state=${nowTime}&returnurl=//home.m.jd.com/myJd/newhome.action?sceneval=2&ufc=&/myJd/home.action`,
123 | headers: {
124 | Connection: 'Keep-Alive',
125 | 'Content-Type': 'application/x-www-form-urlencoded',
126 | Accept: 'application/json, text/plain, */*',
127 | 'Accept-Language': 'zh-cn',
128 | Referer: taskUrl,
129 | 'User-Agent': this.ua,
130 | Host: 'plogin.m.jd.com',
131 | Cookie: this.cookies,
132 | },
133 | });
134 | const configHeaders = configRes.headers;
135 | const configData = configRes.body;
136 |
137 | this.token = configData.token;
138 | if (this.token)
139 | this.QRCode = await QRCode.toDataURL(
140 | `https://plogin.m.jd.com/cgi-bin/m/tmauth?appid=300&client_type=m&token=${this.token}`
141 | );
142 | const cookies = configHeaders['set-cookie'][0];
143 | this.okl_token = cookies.substring(cookies.indexOf('=') + 1, cookies.indexOf(';'));
144 | }
145 |
146 | async checkQRLogin() {
147 | if(true){
148 | return {
149 | errcode: 200,
150 | message: '扫码登录已关闭,请自行抓包手动CK登录',
151 | };
152 | }
153 | if (!this.token || !this.okl_token || !this.cookies) {
154 | throw new Error('初始化登录请求失败!');
155 | }
156 | const nowTime = Date.now();
157 | const loginUrl = `https://plogin.m.jd.com/login/login?appid=300&returnurl=https://wqlogin2.jd.com/passport/LoginRedirect?state=${nowTime}&returnurl=//home.m.jd.com/myJd/newhome.action?sceneval=2&ufc=&/myJd/home.action&source=wq_passport`;
158 | const getUserCookieUrl = `https://plogin.m.jd.com/cgi-bin/m/tmauthchecktoken?&token=${this.token}&ou_state=0&okl_token=${this.okl_token}`;
159 | const response = await api({
160 | method: 'POST',
161 | url: getUserCookieUrl,
162 | body: `lang=chs&appid=300&source=wq_passport&returnurl=https://wqlogin2.jd.com/passport/LoginRedirect?state=${nowTime}&returnurl=//home.m.jd.com/myJd/newhome.action?sceneval=2&ufc=&/myJd/home.action`,
163 | headers: {
164 | Connection: 'Keep-Alive',
165 | 'Content-Type': 'application/x-www-form-urlencoded; Charset=UTF-8',
166 | Accept: 'application/json, text/plain, */*',
167 | 'Accept-Language': 'zh-cn',
168 | Referer: loginUrl,
169 | 'User-Agent': this.ua,
170 | Cookie: this.cookies,
171 | },
172 | });
173 | const data = response.body;
174 | const headers = response.headers;
175 | if (data.errcode === 0) {
176 | const pt_key = headers['set-cookie'][1];
177 | this.pt_key = pt_key.substring(pt_key.indexOf('=') + 1, pt_key.indexOf(';'));
178 | const pt_pin = headers['set-cookie'][2];
179 | this.pt_pin = pt_pin.substring(pt_pin.indexOf('=') + 1, pt_pin.indexOf(';'));
180 | this.cookie = 'pt_key=' + this.pt_key + ';pt_pin=' + this.pt_pin + ';';
181 |
182 | const result = await this.CKLogin();
183 | result.errcode = 0;
184 | return result;
185 | }
186 |
187 | return {
188 | errcode: data.errcode,
189 | message: data.message,
190 | };
191 | }
192 |
193 | async CKLogin() {
194 | let message;
195 | await this.#getNickname();
196 | const envs = await getEnvs();
197 | const poolInfo = await User.getPoolInfo();
198 | const env = await envs.find((item) => item.value.match(/pt_pin=(.*?);/)[1] === this.pt_pin);
199 | if (!env) {
200 | // 新用户
201 | if (!poolInfo.allowAdd) {
202 | throw new UserError('管理员已关闭注册,去其他地方看看吧', 210, 200);
203 | } else if (poolInfo.marginCount === 0) {
204 | throw new UserError('本站已到达注册上限,你来晚啦', 211, 200);
205 | } else {
206 | const remarks = `remark=${this.nickName};`;
207 | const body = await addEnv(this.cookie, remarks);
208 | if (body.code !== 200) {
209 | throw new UserError(body.message || '添加账户错误,请重试', 220, body.code || 200);
210 | }
211 | this.eid = body.data[0]._id;
212 | this.timestamp = body.data[0].timestamp;
213 | message = `注册成功,${this.nickName}`;
214 | this.#sendNotify('Ninja 运行通知', `用户 ${this.nickName}(${decodeURIComponent(this.pt_pin)}) 已上线`);
215 | }
216 | } else {
217 | this.eid = env._id;
218 | const body = await updateEnv(this.cookie, this.eid);
219 | if (body.code !== 200) {
220 | throw new UserError(body.message || '更新账户错误,请重试', 221, body.code || 200);
221 | }
222 | this.timestamp = body.data.timestamp;
223 | message = `欢迎回来,${this.nickName}`;
224 | this.#sendNotify('Ninja 运行通知', `用户 ${this.nickName}(${decodeURIComponent(this.pt_pin)}) 已更新 CK`);
225 | }
226 | return {
227 | nickName: this.nickName,
228 | eid: this.eid,
229 | timestamp: this.timestamp,
230 | message,
231 | };
232 | }
233 |
234 | async getUserInfoByEid() {
235 | const envs = await getEnvs();
236 | const env = await envs.find((item) => item._id === this.eid);
237 | if (!env) {
238 | throw new UserError('没有找到这个账户,重新登录试试看哦', 230, 200);
239 | }
240 | this.cookie = env.value;
241 | this.timestamp = env.timestamp;
242 | const remarks = env.remarks;
243 | if (remarks) {
244 | this.remark = remarks.match(/remark=(.*?);/) && remarks.match(/remark=(.*?);/)[1];
245 | }
246 | await this.#getNickname();
247 | return {
248 | nickName: this.nickName,
249 | eid: this.eid,
250 | timestamp: this.timestamp,
251 | remark: this.remark,
252 | };
253 | }
254 |
255 | async updateRemark() {
256 | if (!this.eid || !this.remark || this.remark.replace(/(^\s*)|(\s*$)/g, '') === '') {
257 | throw new UserError('eid参数错误', 240, 200);
258 | }
259 |
260 | const envs = await getEnvs();
261 | const env = await envs.find((item) => item._id === this.eid);
262 | if (!env) {
263 | throw new UserError('没有找到这个ck账户,重新登录试试看哦', 230, 200);
264 | }
265 | this.cookie = env.value;
266 |
267 | const remarks = `remark=${this.remark};`;
268 |
269 | const updateEnvBody = await updateEnv(this.cookie, this.eid, remarks);
270 | if (updateEnvBody.code !== 200) {
271 | throw new UserError('ck更新/上传备注出错,请重试', 241, 200);
272 | }
273 |
274 | return {
275 | message: 'ck更新/上传备注成功',
276 | };
277 | }
278 |
279 | async delUserByEid() {
280 | await this.getUserInfoByEid();
281 | const body = await delEnv(this.eid);
282 | if (body.code !== 200) {
283 | throw new UserError(body.message || '删除账户错误,请重试', 240, body.code || 200);
284 | }
285 | this.#sendNotify('Ninja 运行通知', `用户 ${this.nickName}(${decodeURIComponent(this.pt_pin)}) 删号跑路了`);
286 | return {
287 | message: '账户已移除',
288 | };
289 | }
290 |
291 | /////////////////////////////////////////////////
292 | // 新增同步方法
293 | async WSCKLogin() {
294 | let message;
295 | await this.#getWSCKCheck();
296 | const envs = await getWSCKEnvs();// 1
297 | const poolInfo = await User.getPoolInfo();
298 | const env = await envs.find((item) => item.value.match(/pin=(.*?);/)[1] === this.pin);
299 | if (!env) {
300 | // 新用户
301 | if (!poolInfo.allowWSCKAdd) {
302 | throw new UserError('管理员已关闭注册,去其他地方看看吧', 210, 200);
303 | } else if (poolInfo.marginWSCKCount === 0) {
304 | throw new UserError('本站已到达注册上限,你来晚啦', 211, 200);
305 | } else {
306 | const remarks = `remark=${this.nickName};`;
307 | const body = await addWSCKEnv(this.jdwsck, remarks);
308 | if (body.code !== 200) {
309 | throw new UserError(body.message || '添加账户错误,请重试', 220, body.code || 200);
310 | }
311 | this.wseid = body.data[0]._id;
312 | this.timestamp = body.data[0].timestamp;
313 | message = `录入成功,${this.pin}`;
314 | this.#sendNotify('Ninja 运行通知', `用户 ${this.pin} WSCK 添加成功`);
315 | }
316 | } else {
317 | this.wseid = env._id;
318 | const body = await updateWSCKEnv(this.jdwsck, this.wseid);
319 | if (body.code !== 200) {
320 | throw new UserError(body.message || '更新账户错误,请重试', 221, body.code || 200);
321 | }
322 | this.timestamp = body.data.timestamp;
323 | message = `欢迎回来,${this.nickName}`;
324 | this.#sendNotify('Ninja 运行通知', `用户 ${this.pin} 已更新 WSCK`);
325 | }
326 |
327 |
328 | return {
329 | nickName: this.nickName,
330 | eid: this.eid,
331 | wseid: this.wseid,
332 | timestamp: this.timestamp,
333 | message,
334 | };
335 | }
336 |
337 | //不查nickname了,用remark代替
338 | async getWSCKUserInfoByEid() {
339 | const envs = await getWSCKEnvs();
340 | const env = await envs.find((item) => item._id === this.wseid);
341 | if (!env) {
342 | throw new UserError('没有找到这个账户,重新登录试试看哦', 230, 200);
343 | }
344 | this.jdwsck = env.value;
345 | this.timestamp = env.timestamp;
346 | const remarks = env.remarks;
347 | if (remarks) {
348 | this.remark = remarks.match(/remark=(.*?);/) && remarks.match(/remark=(.*?);/)[1];
349 | }
350 | // await this.#getNickname();
351 | return {
352 | nickName: this.remark,
353 | wseid: this.wseid,
354 | timestamp: this.timestamp,
355 | remark: this.remark,
356 | };
357 | }
358 |
359 | async updateWSCKRemark() {
360 | if (!this.wseid || !this.remark || this.remark.replace(/(^\s*)|(\s*$)/g, '') === '') {
361 | throw new UserError('wseid参数错误', 240, 200);
362 | }
363 |
364 | const envs = await getWSCKEnvs();
365 | const env = await envs.find((item) => item._id === this.wseid);
366 | if (!env) {
367 | throw new UserError('没有找到这个wskey账户,重新登录试试看哦', 230, 200);
368 | }
369 | this.jdwsck = env.value;
370 |
371 | const remarks = `remark=${this.remark};`;
372 |
373 | const updateEnvBody = await updateWSCKEnv(this.jdwsck, this.wseid, remarks);
374 | if (updateEnvBody.code !== 200) {
375 | throw new UserError('wskey更新/上传备注出错,请重试', 241, 200);
376 | }
377 |
378 | return {
379 | message: 'wskey更新/上传备注成功',
380 | };
381 | }
382 |
383 | async delWSCKUserByEid() {
384 | await this.getWSCKUserInfoByEid();
385 | const body = await delWSCKEnv(this.wseid);
386 | if (body.code !== 200) {
387 | throw new UserError(body.message || '删除账户错误,请重试', 240, body.code || 200);
388 | }
389 | this.#sendNotify('Ninja 运行通知', `用户 ${this.remark}(${decodeURIComponent(this.remark)}) 删号跑路了,CK将无法自动更新并会在不知道那天内自动失效`);
390 | return {
391 | message: 'wskey账户已移除',
392 | };
393 | }
394 |
395 | /////////////////////////////////////////////////
396 |
397 | static async getPoolInfo() {
398 | const count = await getEnvsCount();
399 | const countWSCK = await getWSCKEnvsCount();
400 | const allowCount = (process.env.ALLOW_NUM || 40) - count;
401 | const allowWSCKCount = (process.env.ALLOW_WSCK_NUM || 40) - countWSCK;
402 | return {
403 | marginCount: allowCount >= 0 ? allowCount : 0,
404 | marginWSCKCount: allowWSCKCount >= 0 ? allowWSCKCount : 0,
405 | allowAdd: Boolean(process.env.ALLOW_ADD) || false,
406 | allowWSCKAdd: Boolean(process.env.ALLOW_WSCK_ADD) || false,
407 | showQR: Boolean(process.env.SHOW_QR) || false,
408 | showWSCK: Boolean(process.env.SHOW_WSCK) || false,
409 | showCK: Boolean(process.env.SHOW_CK) || false,
410 | };
411 | }
412 |
413 | static async getUsers() {
414 | const envs = await getEnvs();
415 | const result = envs.map(async (env) => {
416 | const user = new User({ cookie: env.value, remarks: env.remarks });
417 | await user.#getNickname(true);
418 | return {
419 | pt_pin: user.pt_pin,
420 | nickName: user.nickName,
421 | remark: user.remark || user.nickName,
422 | };
423 | });
424 | return Promise.all(result);
425 | }
426 |
427 | async #getNickname(nocheck) {
428 | let body;
429 | let body_bak;
430 | body = await api({
431 | url: `https://me-api.jd.com/user_new/info/GetJDUserInfoUnion?orgFlag=JD_PinGou_New&callSource=mainorder&channel=4&isHomewhite=0&sceneval=2&_=${Date.now()}&sceneval=2&g_login_type=1&g_ty=ls`,
432 | headers: {
433 | Accept: '*/*',
434 | 'Accept-Encoding': 'gzip, deflate, br',
435 | 'Accept-Language': 'zh-cn',
436 | Connection: 'keep-alive',
437 | Cookie: this.cookie,
438 | Referer: 'https://home.m.jd.com/myJd/newhome.action',
439 | 'User-Agent':
440 | 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.111 Safari/537.36',
441 | Host: 'me-api.jd.com',
442 | },
443 | }).json();
444 |
445 | if (!body.data?.userInfo && !nocheck) {
446 | body_bak = await api({
447 | url: `https://wq.jd.com/user_new/info/GetJDUserInfoUnion?orgFlag=JD_PinGou_New&callSource=mainorder`,
448 | headers: {
449 | Connection: 'keep-alive',
450 | Cookie: this.cookie,
451 | Referer: 'https://home.m.jd.com/myJd/home.action',
452 | 'User-Agent':
453 | 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.111 Safari/537.36',
454 | },
455 | }).json();
456 | }
457 |
458 | if (!body.data?.userInfo && !body_bak?.data.userInfo && this.jdwsck && !nocheck) {
459 | throw new UserError('获取用户信息失败,请检查您的 wskey !', 201, 200);
460 | } else if (!body.data?.userInfo && !body_bak?.data.userInfo && !nocheck) {
461 | throw new UserError('获取用户信息失败,请检查您的 cookie !', 201, 200);
462 | }
463 | this.nickName = (body.data?.userInfo.baseInfo.nickname || body_bak?.data.userInfo.baseInfo.nickname) || decodeURIComponent(this.pt_pin);
464 |
465 | // if (!body.data?.userInfo && this.jdwsck) {
466 | // throw new UserError('获取用户信息失败,请检查您的 wskey !', 201, 200);
467 | // } else if (!body.data?.userInfo && !nocheck) {
468 | // throw new UserError('获取用户信息失败,请检查您的 cookie !', 201, 200);
469 | // }
470 | // this.nickName = body.data?.userInfo.baseInfo.nickname || decodeURIComponent(this.pt_pin);
471 | }
472 |
473 | #formatSetCookies(headers, body) {
474 | return new Promise((resolve) => {
475 | let guid, lsid, ls_token;
476 | this.#s_token = body.s_token;
477 | guid = headers['set-cookie'][0];
478 | guid = guid.substring(guid.indexOf('=') + 1, guid.indexOf(';'));
479 | lsid = headers['set-cookie'][2];
480 | lsid = lsid.substring(lsid.indexOf('=') + 1, lsid.indexOf(';'));
481 | ls_token = headers['set-cookie'][3];
482 | ls_token = ls_token.substring(ls_token.indexOf('=') + 1, ls_token.indexOf(';'));
483 | this.cookies = `guid=${guid};lang=chs;lsid=${lsid};ls_token=${ls_token};`;
484 | resolve();
485 | });
486 | }
487 |
488 | #sendNotify(title, content) {
489 | const notify = process.env.NINJA_NOTIFY || true;
490 | if (!notify) {
491 | console.log('Ninja 通知已关闭\n' + title + '\n' + content + '\n' + '已跳过发送');
492 | return;
493 | }
494 | exec(`${notifyFile} "${title}" "${content}"`, (error, stdout, stderr) => {
495 | if (error) {
496 | console.log(stderr);
497 | } else {
498 | console.log(stdout);
499 | }
500 | });
501 | }
502 | //////////////////////////////////////////////
503 | async #getWSCKCheck() {
504 | const s = await api({url: `https://pan.smxy.xyz/sign`}).json();
505 | const clientVersion = s['clientVersion']
506 | const client = s['client']
507 | const sv = s['sv']
508 | const st = s['st']
509 | const uuid = s['uuid']
510 | const sign = s['sign']
511 | if (!sv||!st||!uuid||!sign) {
512 | throw new UserError('获取签名失败,请等待Ninja修理 !', 200, 200);
513 | }
514 | const body = await api({
515 | method: 'POST',
516 | url: `https://api.m.jd.com/client.action?functionId=genToken&clientVersion=${clientVersion}&client=${client}&uuid=${uuid}&st=${st}&sign=${sign}&sv=${sv}`,
517 | body: 'body=%7B%22action%22%3A%22to%22%2C%22to%22%3A%22https%253A%252F%252Fplogin.m.jd.com%252Fcgi-bin%252Fm%252Fthirdapp_auth_page%253Ftoken%253DAAEAIEijIw6wxF2s3bNKF0bmGsI8xfw6hkQT6Ui2QVP7z1Xg%2526client_type%253Dandroid%2526appid%253D879%2526appup_type%253D1%22%7D&',
518 | headers: {
519 | Cookie: this.jdwsck,
520 | 'User-Agent': 'okhttp/3.12.1;jdmall;android;version/10.1.2;build/89743;screen/1440x3007;os/11;network/wifi;',
521 | 'Content-type': 'application/x-www-form-urlencoded; charset=UTF-8',
522 | 'Accept-Charset': 'UTF-8',
523 | 'Accept-Encoding': 'br,gzip,deflate'
524 | },
525 | }).json();
526 | const response = await got1({
527 | followRedirect:false,
528 | url: `https://un.m.jd.com/cgi-bin/app/appjmp?tokenKey=${body['tokenKey']}&to=https://plogin.m.jd.com/cgi-bin/m/thirdapp_auth_page?token=AAEAIEijIw6wxF2s3bNKF0bmGsI8xfw6hkQT6Ui2QVP7z1Xg&client_type=android&appid=879&appup_type=1`,
529 | headers: {
530 | 'User-Agent': 'okhttp/3.12.1;jdmall;android;version/10.1.2;build/89743;screen/1440x3007;os/11;network/wifi;',
531 | Accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3',
532 | 'Accept-Charset': 'UTF-8',
533 | 'Accept-Encoding': 'br,gzip,deflate'
534 | },
535 | });
536 | const headers = response.headers;
537 | if (headers['set-cookie']) {
538 | const pt_key = headers['set-cookie'][2];
539 | this.pt_key = pt_key.substring(pt_key.indexOf('=') + 1, pt_key.indexOf(';'));
540 | const pt_pin = headers['set-cookie'][3];
541 | this.pt_pin = pt_pin.substring(pt_pin.indexOf('=') + 1, pt_pin.indexOf(';'));
542 | }
543 | if (this.pt_key&&this.pt_pin) {
544 | this.cookie = 'pt_key=' + this.pt_key + ';pt_pin=' + this.pt_pin + ';';
545 | const result = await this.CKLogin();
546 | this.eid = result.eid
547 | result.errcode = 0;
548 | return result;
549 | }
550 | }
551 | ////////////////////////////////////////////////
552 | };
553 |
554 | class UserError extends Error {
555 | constructor(message, status, statusCode) {
556 | super(message);
557 | this.name = 'UserError';
558 | this.status = status;
559 | this.statusCode = statusCode || 200;
560 | }
561 | }
562 |
--------------------------------------------------------------------------------
/backend/sendNotify.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: lxk0301 https://gitee.com/lxk0301
3 | * @Date: 2020-08-19 16:12:40
4 | * @Last Modified by: whyour
5 | * @Last Modified time: 2021-5-1 15:00:54
6 | * sendNotify 推送通知功能
7 | * @param text 通知头
8 | * @param desp 通知体
9 | * @param params 某些推送通知方式点击弹窗可跳转, 例:{ url: 'https://abc.com' }
10 | * @param author 作者仓库等信息 例:`本通知 By:https://github.com/whyour/qinglong`
11 | */
12 |
13 | const querystring = require('querystring');
14 | const $ = new Env();
15 | const timeout = 15000; //超时时间(单位毫秒)
16 | // =======================================go-cqhttp通知设置区域===========================================
17 | //gobot_url 填写请求地址http://127.0.0.1/send_private_msg
18 | //gobot_token 填写在go-cqhttp文件设置的访问密钥
19 | //gobot_qq 填写推送到个人QQ或者QQ群号
20 | //go-cqhttp相关API https://docs.go-cqhttp.org/api
21 | let GOBOT_URL = ''; // 推送到个人QQ: http://127.0.0.1/send_private_msg 群:http://127.0.0.1/send_group_msg
22 | let GOBOT_TOKEN = ''; //访问密钥
23 | let GOBOT_QQ = ''; // 如果GOBOT_URL设置 /send_private_msg 则需要填入 user_id=个人QQ 相反如果是 /send_group_msg 则需要填入 group_id=QQ群
24 |
25 | // =======================================微信server酱通知设置区域===========================================
26 | //此处填你申请的SCKEY.
27 | //(环境变量名 PUSH_KEY)
28 | let SCKEY = '';
29 |
30 | // =======================================Bark App通知设置区域===========================================
31 | //此处填你BarkAPP的信息(IP/设备码,例如:https://api.day.app/XXXXXXXX)
32 | let BARK_PUSH = '';
33 | //BARK app推送铃声,铃声列表去APP查看复制填写
34 | let BARK_SOUND = '';
35 | //BARK app推送消息的分组, 默认为"QingLong"
36 | let BARK_GROUP = 'QingLong';
37 |
38 | // =======================================telegram机器人通知设置区域===========================================
39 | //此处填你telegram bot 的Token,telegram机器人通知推送必填项.例如:1077xxx4424:AAFjv0FcqxxxxxxgEMGfi22B4yh15R5uw
40 | //(环境变量名 TG_BOT_TOKEN)
41 | let TG_BOT_TOKEN = '';
42 | //此处填你接收通知消息的telegram用户的id,telegram机器人通知推送必填项.例如:129xxx206
43 | //(环境变量名 TG_USER_ID)
44 | let TG_USER_ID = '';
45 | //tg推送HTTP代理设置(不懂可忽略,telegram机器人通知推送功能中非必填)
46 | let TG_PROXY_HOST = ''; //例如:127.0.0.1(环境变量名:TG_PROXY_HOST)
47 | let TG_PROXY_PORT = ''; //例如:1080(环境变量名:TG_PROXY_PORT)
48 | let TG_PROXY_AUTH = ''; //tg代理配置认证参数
49 | //Telegram api自建的反向代理地址(不懂可忽略,telegram机器人通知推送功能中非必填),默认tg官方api(环境变量名:TG_API_HOST)
50 | let TG_API_HOST = 'api.telegram.org';
51 | // =======================================钉钉机器人通知设置区域===========================================
52 | //此处填你钉钉 bot 的webhook,例如:5a544165465465645d0f31dca676e7bd07415asdasd
53 | //(环境变量名 DD_BOT_TOKEN)
54 | let DD_BOT_TOKEN = '';
55 | //密钥,机器人安全设置页面,加签一栏下面显示的SEC开头的字符串
56 | let DD_BOT_SECRET = '';
57 |
58 | // =======================================企业微信机器人通知设置区域===========================================
59 | //此处填你企业微信机器人的 webhook(详见文档 https://work.weixin.qq.com/api/doc/90000/90136/91770),例如:693a91f6-7xxx-4bc4-97a0-0ec2sifa5aaa
60 | //(环境变量名 QYWX_KEY)
61 | let QYWX_KEY = '';
62 |
63 | // =======================================企业微信应用消息通知设置区域===========================================
64 | /*
65 | 此处填你企业微信应用消息的值(详见文档 https://work.weixin.qq.com/api/doc/90000/90135/90236)
66 | 环境变量名 QYWX_AM依次填入 corpid,corpsecret,touser(注:多个成员ID使用|隔开),agentid,消息类型(选填,不填默认文本消息类型)
67 | 注意用,号隔开(英文输入法的逗号),例如:wwcff56746d9adwers,B-791548lnzXBE6_BWfxdf3kSTMJr9vFEPKAbh6WERQ,mingcheng,1000001,2COXgjH2UIfERF2zxrtUOKgQ9XklUqMdGSWLBoW_lSDAdafat
68 | 可选推送消息类型(推荐使用图文消息(mpnews)):
69 | - 文本卡片消息: 0 (数字零)
70 | - 文本消息: 1 (数字一)
71 | - 图文消息(mpnews): 素材库图片id, 可查看此教程(http://note.youdao.com/s/HMiudGkb)或者(https://note.youdao.com/ynoteshare1/index.html?id=1a0c8aff284ad28cbd011b29b3ad0191&type=note)
72 | */
73 | let QYWX_AM = '';
74 |
75 | // =======================================iGot聚合推送通知设置区域===========================================
76 | //此处填您iGot的信息(推送key,例如:https://push.hellyw.com/XXXXXXXX)
77 | let IGOT_PUSH_KEY = '';
78 |
79 | // =======================================push+设置区域=======================================
80 | //官方文档:http://www.pushplus.plus/
81 | //PUSH_PLUS_TOKEN:微信扫码登录后一对一推送或一对多推送下面的token(您的Token),不提供PUSH_PLUS_USER则默认为一对一推送
82 | //PUSH_PLUS_USER: 一对多推送的“群组编码”(一对多推送下面->您的群组(如无则新建)->群组编码,如果您是创建群组人。也需点击“查看二维码”扫描绑定,否则不能接受群组消息推送)
83 | let PUSH_PLUS_TOKEN = '';
84 | let PUSH_PLUS_USER = '';
85 |
86 | //==========================云端环境变量的判断与接收=========================
87 | if (process.env.GOBOT_URL) {
88 | GOBOT_URL = process.env.GOBOT_URL;
89 | }
90 | if (process.env.GOBOT_TOKEN) {
91 | GOBOT_TOKEN = process.env.GOBOT_TOKEN;
92 | }
93 | if (process.env.GOBOT_QQ) {
94 | GOBOT_QQ = process.env.GOBOT_QQ;
95 | }
96 |
97 | if (process.env.PUSH_KEY) {
98 | SCKEY = process.env.PUSH_KEY;
99 | }
100 |
101 | if (process.env.QQ_SKEY) {
102 | QQ_SKEY = process.env.QQ_SKEY;
103 | }
104 |
105 | if (process.env.QQ_MODE) {
106 | QQ_MODE = process.env.QQ_MODE;
107 | }
108 |
109 | if (process.env.BARK_PUSH) {
110 | if (process.env.BARK_PUSH.indexOf('https') > -1 || process.env.BARK_PUSH.indexOf('http') > -1) {
111 | //兼容BARK自建用户
112 | BARK_PUSH = process.env.BARK_PUSH;
113 | } else {
114 | BARK_PUSH = `https://api.day.app/${process.env.BARK_PUSH}`;
115 | }
116 | if (process.env.BARK_SOUND) {
117 | BARK_SOUND = process.env.BARK_SOUND;
118 | }
119 | if (process.env.BARK_GROUP) {
120 | BARK_GROUP = process.env.BARK_GROUP;
121 | }
122 | } else {
123 | if (BARK_PUSH && BARK_PUSH.indexOf('https') === -1 && BARK_PUSH.indexOf('http') === -1) {
124 | //兼容BARK本地用户只填写设备码的情况
125 | BARK_PUSH = `https://api.day.app/${BARK_PUSH}`;
126 | }
127 | }
128 | if (process.env.TG_BOT_TOKEN) {
129 | TG_BOT_TOKEN = process.env.TG_BOT_TOKEN;
130 | }
131 | if (process.env.TG_USER_ID) {
132 | TG_USER_ID = process.env.TG_USER_ID;
133 | }
134 | if (process.env.TG_PROXY_AUTH) TG_PROXY_AUTH = process.env.TG_PROXY_AUTH;
135 | if (process.env.TG_PROXY_HOST) TG_PROXY_HOST = process.env.TG_PROXY_HOST;
136 | if (process.env.TG_PROXY_PORT) TG_PROXY_PORT = process.env.TG_PROXY_PORT;
137 | if (process.env.TG_API_HOST) TG_API_HOST = process.env.TG_API_HOST;
138 |
139 | if (process.env.DD_BOT_TOKEN) {
140 | DD_BOT_TOKEN = process.env.DD_BOT_TOKEN;
141 | if (process.env.DD_BOT_SECRET) {
142 | DD_BOT_SECRET = process.env.DD_BOT_SECRET;
143 | }
144 | }
145 |
146 | if (process.env.QYWX_KEY) {
147 | QYWX_KEY = process.env.QYWX_KEY;
148 | }
149 |
150 | if (process.env.QYWX_AM) {
151 | QYWX_AM = process.env.QYWX_AM;
152 | }
153 |
154 | if (process.env.IGOT_PUSH_KEY) {
155 | IGOT_PUSH_KEY = process.env.IGOT_PUSH_KEY;
156 | }
157 |
158 | if (process.env.PUSH_PLUS_TOKEN) {
159 | PUSH_PLUS_TOKEN = process.env.PUSH_PLUS_TOKEN;
160 | }
161 | if (process.env.PUSH_PLUS_USER) {
162 | PUSH_PLUS_USER = process.env.PUSH_PLUS_USER;
163 | }
164 | //==========================云端环境变量的判断与接收=========================
165 |
166 | /**
167 | * sendNotify 推送通知功能
168 | * @param text 通知头
169 | * @param desp 通知体
170 | * @param params 某些推送通知方式点击弹窗可跳转, 例:{ url: 'https://abc.com' }
171 | * @param author 作者仓库等信息 例:`本通知 By:https://github.com/whyour/qinglong`
172 | * @returns {Promise}
173 | */
174 | async function sendNotify(text, desp, params = {}, author = '\n\n本通知 By:https://github.com/whyour/qinglong') {
175 | try {
176 | const notifySkipList = process.env.NOTIFY_SKIP_LIST ? process.env.NOTIFY_SKIP_LIST.split('&') : [];
177 | const titleIndex = notifySkipList.findIndex((item) => item === text);
178 |
179 | if (titleIndex !== -1) {
180 | console.log(`${text} 在推送黑名单中,已跳过推送`);
181 | return;
182 | }
183 |
184 | const got = require('got');
185 |
186 | const body = await got('http://localhost:5701/api/users').json();
187 | const users = body.data;
188 |
189 | for (const user of users) {
190 | if (user.pt_pin && user.nickName && user.remark) {
191 | desp = desp.replace(new RegExp(`${user.pt_pin}|${user.nickName}`, 'gm'), user.remark);
192 | }
193 | }
194 |
195 | } catch (error) {
196 | console.error(error);
197 | }
198 |
199 | //提供6种通知
200 | desp += author; //增加作者信息,防止被贩卖等
201 | await Promise.all([
202 | serverNotify(text, desp), //微信server酱
203 | pushPlusNotify(text, desp), //pushplus(推送加)
204 | ]);
205 | //由于上述两种微信通知需点击进去才能查看到详情,故text(标题内容)携带了账号序号以及昵称信息,方便不点击也可知道是哪个京东哪个活动
206 | text = text.match(/.*?(?=\s?-)/g) ? text.match(/.*?(?=\s?-)/g)[0] : text;
207 | await Promise.all([
208 | BarkNotify(text, desp, params), //iOS Bark APP
209 | tgBotNotify(text, desp), //telegram 机器人
210 | ddBotNotify(text, desp), //钉钉机器人
211 | qywxBotNotify(text, desp), //企业微信机器人
212 | qywxamNotify(text, desp), //企业微信应用消息推送
213 | iGotNotify(text, desp, params), //iGot
214 | gobotNotify(text, desp), //go-cqhttp
215 | ]);
216 | }
217 |
218 | function gobotNotify(text, desp, time = 2100) {
219 | return new Promise((resolve) => {
220 | if (GOBOT_URL) {
221 | const options = {
222 | url: `${GOBOT_URL}?access_token=${GOBOT_TOKEN}&${GOBOT_QQ}`,
223 | body: `message=${text}\n${desp}`,
224 | headers: {
225 | 'Content-Type': 'application/x-www-form-urlencoded',
226 | },
227 | timeout,
228 | };
229 | setTimeout(() => {
230 | $.post(options, (err, resp, data) => {
231 | try {
232 | if (err) {
233 | console.log('发送go-cqhttp通知调用API失败!!\n');
234 | console.log(err);
235 | } else {
236 | data = JSON.parse(data);
237 | if (data.retcode === 0) {
238 | console.log('go-cqhttp发送通知消息成功🎉\n');
239 | } else if (data.retcode === 100) {
240 | console.log(`go-cqhttp发送通知消息异常: ${data.errmsg}\n`);
241 | } else {
242 | console.log(`go-cqhttp发送通知消息异常\n${JSON.stringify(data)}`);
243 | }
244 | }
245 | } catch (e) {
246 | $.logErr(e, resp);
247 | } finally {
248 | resolve(data);
249 | }
250 | });
251 | }, time);
252 | } else {
253 | resolve();
254 | }
255 | });
256 | }
257 |
258 | function serverNotify(text, desp, time = 2100) {
259 | return new Promise((resolve) => {
260 | if (SCKEY) {
261 | //微信server酱推送通知一个\n不会换行,需要两个\n才能换行,故做此替换
262 | desp = desp.replace(/[\n\r]/g, '\n\n');
263 | const options = {
264 | url: SCKEY.includes('SCT') ? `https://sctapi.ftqq.com/${SCKEY}.send` : `https://sc.ftqq.com/${SCKEY}.send`,
265 | body: `text=${text}&desp=${desp}`,
266 | headers: {
267 | 'Content-Type': 'application/x-www-form-urlencoded',
268 | },
269 | timeout,
270 | };
271 | setTimeout(() => {
272 | $.post(options, (err, resp, data) => {
273 | try {
274 | if (err) {
275 | console.log('发送通知调用API失败!!\n');
276 | console.log(err);
277 | } else {
278 | data = JSON.parse(data);
279 | //server酱和Server酱·Turbo版的返回json格式不太一样
280 | if (data.errno === 0 || data.data.errno === 0) {
281 | console.log('server酱发送通知消息成功🎉\n');
282 | } else if (data.errno === 1024) {
283 | // 一分钟内发送相同的内容会触发
284 | console.log(`server酱发送通知消息异常: ${data.errmsg}\n`);
285 | } else {
286 | console.log(`server酱发送通知消息异常\n${JSON.stringify(data)}`);
287 | }
288 | }
289 | } catch (e) {
290 | $.logErr(e, resp);
291 | } finally {
292 | resolve(data);
293 | }
294 | });
295 | }, time);
296 | } else {
297 | resolve();
298 | }
299 | });
300 | }
301 |
302 | function CoolPush(text, desp) {
303 | return new Promise((resolve) => {
304 | if (QQ_SKEY) {
305 | let options = {
306 | url: `https://push.xuthus.cc/${QQ_MODE}/${QQ_SKEY}`,
307 | headers: {
308 | 'Content-Type': 'application/json',
309 | },
310 | };
311 |
312 | // 已知敏感词
313 | text = text.replace(/京豆/g, '豆豆');
314 | desp = desp.replace(/京豆/g, '');
315 | desp = desp.replace(/🐶/g, '');
316 | desp = desp.replace(/红包/g, 'H包');
317 |
318 | switch (QQ_MODE) {
319 | case 'email':
320 | options.json = {
321 | t: text,
322 | c: desp,
323 | };
324 | break;
325 | default:
326 | options.body = `${text}\n\n${desp}`;
327 | }
328 |
329 | let pushMode = function (t) {
330 | switch (t) {
331 | case 'send':
332 | return '个人';
333 | case 'group':
334 | return 'QQ群';
335 | case 'wx':
336 | return '微信';
337 | case 'ww':
338 | return '企业微信';
339 | case 'email':
340 | return '邮件';
341 | default:
342 | return '未知方式';
343 | }
344 | };
345 |
346 | $.post(options, (err, resp, data) => {
347 | try {
348 | if (err) {
349 | console.log(`发送${pushMode(QQ_MODE)}通知调用API失败!!\n`);
350 | console.log(err);
351 | } else {
352 | data = JSON.parse(data);
353 | if (data.code === 200) {
354 | console.log(`酷推发送${pushMode(QQ_MODE)}通知消息成功🎉\n`);
355 | } else if (data.code === 400) {
356 | console.log(`QQ酷推(Cool Push)发送${pushMode(QQ_MODE)}推送失败:${data.msg}\n`);
357 | } else if (data.code === 503) {
358 | console.log(`QQ酷推出错,${data.message}:${data.data}\n`);
359 | } else {
360 | console.log(`酷推推送异常: ${JSON.stringify(data)}`);
361 | }
362 | }
363 | } catch (e) {
364 | $.logErr(e, resp);
365 | } finally {
366 | resolve(data);
367 | }
368 | });
369 | } else {
370 | resolve();
371 | }
372 | });
373 | }
374 |
375 | function BarkNotify(text, desp, params = {}) {
376 | return new Promise((resolve) => {
377 | if (BARK_PUSH) {
378 | const options = {
379 | url: `${BARK_PUSH}/${encodeURIComponent(text)}/${encodeURIComponent(
380 | desp
381 | )}?sound=${BARK_SOUND}&group=${BARK_GROUP}&${querystring.stringify(params)}`,
382 | headers: {
383 | 'Content-Type': 'application/x-www-form-urlencoded',
384 | },
385 | timeout,
386 | };
387 | $.get(options, (err, resp, data) => {
388 | try {
389 | if (err) {
390 | console.log('Bark APP发送通知调用API失败!!\n');
391 | console.log(err);
392 | } else {
393 | data = JSON.parse(data);
394 | if (data.code === 200) {
395 | console.log('Bark APP发送通知消息成功🎉\n');
396 | } else {
397 | console.log(`${data.message}\n`);
398 | }
399 | }
400 | } catch (e) {
401 | $.logErr(e, resp);
402 | } finally {
403 | resolve();
404 | }
405 | });
406 | } else {
407 | resolve();
408 | }
409 | });
410 | }
411 |
412 | function tgBotNotify(text, desp) {
413 | return new Promise((resolve) => {
414 | if (TG_BOT_TOKEN && TG_USER_ID) {
415 | const options = {
416 | url: `https://${TG_API_HOST}/bot${TG_BOT_TOKEN}/sendMessage`,
417 | body: `chat_id=${TG_USER_ID}&text=${text}\n\n${desp}&disable_web_page_preview=true`,
418 | headers: {
419 | 'Content-Type': 'application/x-www-form-urlencoded',
420 | },
421 | timeout,
422 | };
423 | if (TG_PROXY_HOST && TG_PROXY_PORT) {
424 | const tunnel = require('tunnel');
425 | const agent = {
426 | https: tunnel.httpsOverHttp({
427 | proxy: {
428 | host: TG_PROXY_HOST,
429 | port: TG_PROXY_PORT * 1,
430 | proxyAuth: TG_PROXY_AUTH,
431 | },
432 | }),
433 | };
434 | Object.assign(options, { agent });
435 | }
436 | $.post(options, (err, resp, data) => {
437 | try {
438 | if (err) {
439 | console.log('telegram发送通知消息失败!!\n');
440 | console.log(err);
441 | } else {
442 | data = JSON.parse(data);
443 | if (data.ok) {
444 | console.log('Telegram发送通知消息成功🎉。\n');
445 | } else if (data.error_code === 400) {
446 | console.log('请主动给bot发送一条消息并检查接收用户ID是否正确。\n');
447 | } else if (data.error_code === 401) {
448 | console.log('Telegram bot token 填写错误。\n');
449 | }
450 | }
451 | } catch (e) {
452 | $.logErr(e, resp);
453 | } finally {
454 | resolve(data);
455 | }
456 | });
457 | } else {
458 | resolve();
459 | }
460 | });
461 | }
462 | function ddBotNotify(text, desp) {
463 | return new Promise((resolve) => {
464 | const options = {
465 | url: `https://oapi.dingtalk.com/robot/send?access_token=${DD_BOT_TOKEN}`,
466 | json: {
467 | msgtype: 'text',
468 | text: {
469 | content: ` ${text}\n\n${desp}`,
470 | },
471 | },
472 | headers: {
473 | 'Content-Type': 'application/json',
474 | },
475 | timeout,
476 | };
477 | if (DD_BOT_TOKEN && DD_BOT_SECRET) {
478 | const crypto = require('crypto');
479 | const dateNow = Date.now();
480 | const hmac = crypto.createHmac('sha256', DD_BOT_SECRET);
481 | hmac.update(`${dateNow}\n${DD_BOT_SECRET}`);
482 | const result = encodeURIComponent(hmac.digest('base64'));
483 | options.url = `${options.url}×tamp=${dateNow}&sign=${result}`;
484 | $.post(options, (err, resp, data) => {
485 | try {
486 | if (err) {
487 | console.log('钉钉发送通知消息失败!!\n');
488 | console.log(err);
489 | } else {
490 | data = JSON.parse(data);
491 | if (data.errcode === 0) {
492 | console.log('钉钉发送通知消息成功🎉。\n');
493 | } else {
494 | console.log(`${data.errmsg}\n`);
495 | }
496 | }
497 | } catch (e) {
498 | $.logErr(e, resp);
499 | } finally {
500 | resolve(data);
501 | }
502 | });
503 | } else if (DD_BOT_TOKEN) {
504 | $.post(options, (err, resp, data) => {
505 | try {
506 | if (err) {
507 | console.log('钉钉发送通知消息失败!!\n');
508 | console.log(err);
509 | } else {
510 | data = JSON.parse(data);
511 | if (data.errcode === 0) {
512 | console.log('钉钉发送通知消息完成。\n');
513 | } else {
514 | console.log(`${data.errmsg}\n`);
515 | }
516 | }
517 | } catch (e) {
518 | $.logErr(e, resp);
519 | } finally {
520 | resolve(data);
521 | }
522 | });
523 | } else {
524 | resolve();
525 | }
526 | });
527 | }
528 |
529 | function qywxBotNotify(text, desp) {
530 | return new Promise((resolve) => {
531 | const options = {
532 | url: `https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=${QYWX_KEY}`,
533 | json: {
534 | msgtype: 'text',
535 | text: {
536 | content: ` ${text}\n\n${desp}`,
537 | },
538 | },
539 | headers: {
540 | 'Content-Type': 'application/json',
541 | },
542 | timeout,
543 | };
544 | if (QYWX_KEY) {
545 | $.post(options, (err, resp, data) => {
546 | try {
547 | if (err) {
548 | console.log('企业微信发送通知消息失败!!\n');
549 | console.log(err);
550 | } else {
551 | data = JSON.parse(data);
552 | if (data.errcode === 0) {
553 | console.log('企业微信发送通知消息成功🎉。\n');
554 | } else {
555 | console.log(`${data.errmsg}\n`);
556 | }
557 | }
558 | } catch (e) {
559 | $.logErr(e, resp);
560 | } finally {
561 | resolve(data);
562 | }
563 | });
564 | } else {
565 | resolve();
566 | }
567 | });
568 | }
569 |
570 | function ChangeUserId(desp) {
571 | const QYWX_AM_AY = QYWX_AM.split(',');
572 | if (QYWX_AM_AY[2]) {
573 | const userIdTmp = QYWX_AM_AY[2].split('|');
574 | let userId = '';
575 | for (let i = 0; i < userIdTmp.length; i++) {
576 | const count = '账号' + (i + 1);
577 | const count2 = '签到号 ' + (i + 1);
578 | if (desp.match(count2)) {
579 | userId = userIdTmp[i];
580 | }
581 | }
582 | if (!userId) userId = QYWX_AM_AY[2];
583 | return userId;
584 | } else {
585 | return '@all';
586 | }
587 | }
588 |
589 | function qywxamNotify(text, desp) {
590 | return new Promise((resolve) => {
591 | if (QYWX_AM) {
592 | const QYWX_AM_AY = QYWX_AM.split(',');
593 | const options_accesstoken = {
594 | url: `https://qyapi.weixin.qq.com/cgi-bin/gettoken`,
595 | json: {
596 | corpid: `${QYWX_AM_AY[0]}`,
597 | corpsecret: `${QYWX_AM_AY[1]}`,
598 | },
599 | headers: {
600 | 'Content-Type': 'application/json',
601 | },
602 | timeout,
603 | };
604 | $.post(options_accesstoken, (err, resp, data) => {
605 | html = desp.replace(/\n/g, ' ');
606 | var json = JSON.parse(data);
607 | accesstoken = json.access_token;
608 | let options;
609 |
610 | switch (QYWX_AM_AY[4]) {
611 | case '0':
612 | options = {
613 | msgtype: 'textcard',
614 | textcard: {
615 | title: `${text}`,
616 | description: `${desp}`,
617 | url: 'https://github.com/whyour/qinglong',
618 | btntxt: '更多',
619 | },
620 | };
621 | break;
622 |
623 | case '1':
624 | options = {
625 | msgtype: 'text',
626 | text: {
627 | content: `${text}\n\n${desp}`,
628 | },
629 | };
630 | break;
631 |
632 | default:
633 | options = {
634 | msgtype: 'mpnews',
635 | mpnews: {
636 | articles: [
637 | {
638 | title: `${text}`,
639 | thumb_media_id: `${QYWX_AM_AY[4]}`,
640 | author: `智能助手`,
641 | content_source_url: ``,
642 | content: `${html}`,
643 | digest: `${desp}`,
644 | },
645 | ],
646 | },
647 | };
648 | }
649 | if (!QYWX_AM_AY[4]) {
650 | //如不提供第四个参数,则默认进行文本消息类型推送
651 | options = {
652 | msgtype: 'text',
653 | text: {
654 | content: `${text}\n\n${desp}`,
655 | },
656 | };
657 | }
658 | options = {
659 | url: `https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=${accesstoken}`,
660 | json: {
661 | touser: `${ChangeUserId(desp)}`,
662 | agentid: `${QYWX_AM_AY[3]}`,
663 | safe: '0',
664 | ...options,
665 | },
666 | headers: {
667 | 'Content-Type': 'application/json',
668 | },
669 | };
670 |
671 | $.post(options, (err, resp, data) => {
672 | try {
673 | if (err) {
674 | console.log('成员ID:' + ChangeUserId(desp) + '企业微信应用消息发送通知消息失败!!\n');
675 | console.log(err);
676 | } else {
677 | data = JSON.parse(data);
678 | if (data.errcode === 0) {
679 | console.log('成员ID:' + ChangeUserId(desp) + '企业微信应用消息发送通知消息成功🎉。\n');
680 | } else {
681 | console.log(`${data.errmsg}\n`);
682 | }
683 | }
684 | } catch (e) {
685 | $.logErr(e, resp);
686 | } finally {
687 | resolve(data);
688 | }
689 | });
690 | });
691 | } else {
692 | resolve();
693 | }
694 | });
695 | }
696 |
697 | function iGotNotify(text, desp, params = {}) {
698 | return new Promise((resolve) => {
699 | if (IGOT_PUSH_KEY) {
700 | // 校验传入的IGOT_PUSH_KEY是否有效
701 | const IGOT_PUSH_KEY_REGX = new RegExp('^[a-zA-Z0-9]{24}$');
702 | if (!IGOT_PUSH_KEY_REGX.test(IGOT_PUSH_KEY)) {
703 | console.log('您所提供的IGOT_PUSH_KEY无效\n');
704 | resolve();
705 | return;
706 | }
707 | const options = {
708 | url: `https://push.hellyw.com/${IGOT_PUSH_KEY.toLowerCase()}`,
709 | body: `title=${text}&content=${desp}&${querystring.stringify(params)}`,
710 | headers: {
711 | 'Content-Type': 'application/x-www-form-urlencoded',
712 | },
713 | timeout,
714 | };
715 | $.post(options, (err, resp, data) => {
716 | try {
717 | if (err) {
718 | console.log('发送通知调用API失败!!\n');
719 | console.log(err);
720 | } else {
721 | if (typeof data === 'string') data = JSON.parse(data);
722 | if (data.ret === 0) {
723 | console.log('iGot发送通知消息成功🎉\n');
724 | } else {
725 | console.log(`iGot发送通知消息失败:${data.errMsg}\n`);
726 | }
727 | }
728 | } catch (e) {
729 | $.logErr(e, resp);
730 | } finally {
731 | resolve(data);
732 | }
733 | });
734 | } else {
735 | resolve();
736 | }
737 | });
738 | }
739 |
740 | function pushPlusNotify(text, desp) {
741 | return new Promise((resolve) => {
742 | if (PUSH_PLUS_TOKEN) {
743 | desp = desp.replace(/[\n\r]/g, ' '); // 默认为html, 不支持plaintext
744 | const body = {
745 | token: `${PUSH_PLUS_TOKEN}`,
746 | title: `${text}`,
747 | content: `${desp}`,
748 | topic: `${PUSH_PLUS_USER}`,
749 | };
750 | const options = {
751 | url: `https://www.pushplus.plus/send`,
752 | body: JSON.stringify(body),
753 | headers: {
754 | 'Content-Type': ' application/json',
755 | },
756 | timeout,
757 | };
758 | $.post(options, (err, resp, data) => {
759 | try {
760 | if (err) {
761 | console.log(`push+发送${PUSH_PLUS_USER ? '一对多' : '一对一'}通知消息失败!!\n`);
762 | console.log(err);
763 | } else {
764 | data = JSON.parse(data);
765 | if (data.code === 200) {
766 | console.log(`push+发送${PUSH_PLUS_USER ? '一对多' : '一对一'}通知消息完成。\n`);
767 | } else {
768 | console.log(`push+发送${PUSH_PLUS_USER ? '一对多' : '一对一'}通知消息失败:${data.msg}\n`);
769 | }
770 | }
771 | } catch (e) {
772 | $.logErr(e, resp);
773 | } finally {
774 | resolve(data);
775 | }
776 | });
777 | } else {
778 | resolve();
779 | }
780 | });
781 | }
782 |
783 | module.exports = {
784 | sendNotify,
785 | BARK_PUSH,
786 | };
787 |
788 | // prettier-ignore
789 | function Env(t, s) { return new (class { constructor(t, s) { (this.name = t), (this.data = null), (this.dataFile = 'box.dat'), (this.logs = []), (this.logSeparator = '\n'), (this.startTime = new Date().getTime()), Object.assign(this, s), this.log('', `\ud83d\udd14${this.name}, \u5f00\u59cb!`); } isNode() { return 'undefined' != typeof module && !!module.exports; } isQuanX() { return 'undefined' != typeof $task; } isSurge() { return 'undefined' != typeof $httpClient && 'undefined' == typeof $loon; } isLoon() { return 'undefined' != typeof $loon; } getScript(t) { return new Promise((s) => { $.get({ url: t }, (t, e, i) => s(i)); }); } runScript(t, s) { return new Promise((e) => { let i = this.getdata('@chavy_boxjs_userCfgs.httpapi'); i = i ? i.replace(/\n/g, '').trim() : i; let o = this.getdata('@chavy_boxjs_userCfgs.httpapi_timeout'); (o = o ? 1 * o : 20), (o = s && s.timeout ? s.timeout : o); const [h, a] = i.split('@'), r = { url: `http://${a}/v1/scripting/evaluate`, body: { script_text: t, mock_type: 'cron', timeout: o }, headers: { 'X-Key': h, Accept: '*/*' }, }; $.post(r, (t, s, i) => e(i)); }).catch((t) => this.logErr(t)); } loaddata() { if (!this.isNode()) return {}; { (this.fs = this.fs ? this.fs : require('fs')), (this.path = this.path ? this.path : require('path')); const t = this.path.resolve(this.dataFile), s = this.path.resolve(process.cwd(), this.dataFile), e = this.fs.existsSync(t), i = !e && this.fs.existsSync(s); if (!e && !i) return {}; { const i = e ? t : s; try { return JSON.parse(this.fs.readFileSync(i)); } catch (t) { return {}; } } } } writedata() { if (this.isNode()) { (this.fs = this.fs ? this.fs : require('fs')), (this.path = this.path ? this.path : require('path')); const t = this.path.resolve(this.dataFile), s = this.path.resolve(process.cwd(), this.dataFile), e = this.fs.existsSync(t), i = !e && this.fs.existsSync(s), o = JSON.stringify(this.data); e ? this.fs.writeFileSync(t, o) : i ? this.fs.writeFileSync(s, o) : this.fs.writeFileSync(t, o); } } lodash_get(t, s, e) { const i = s.replace(/\[(\d+)\]/g, '.$1').split('.'); let o = t; for (const t of i) if (((o = Object(o)[t]), void 0 === o)) return e; return o; } lodash_set(t, s, e) { return Object(t) !== t ? t : (Array.isArray(s) || (s = s.toString().match(/[^.[\]]+/g) || []), (s .slice(0, -1) .reduce( (t, e, i) => (Object(t[e]) === t[e] ? t[e] : (t[e] = Math.abs(s[i + 1]) >> 0 == +s[i + 1] ? [] : {})), t )[s[s.length - 1]] = e), t); } getdata(t) { let s = this.getval(t); if (/^@/.test(t)) { const [, e, i] = /^@(.*?)\.(.*?)$/.exec(t), o = e ? this.getval(e) : ''; if (o) try { const t = JSON.parse(o); s = t ? this.lodash_get(t, i, '') : s; } catch (t) { s = ''; } } return s; } setdata(t, s) { let e = !1; if (/^@/.test(s)) { const [, i, o] = /^@(.*?)\.(.*?)$/.exec(s), h = this.getval(i), a = i ? ('null' === h ? null : h || '{}') : '{}'; try { const s = JSON.parse(a); this.lodash_set(s, o, t), (e = this.setval(JSON.stringify(s), i)); } catch (s) { const h = {}; this.lodash_set(h, o, t), (e = this.setval(JSON.stringify(h), i)); } } else e = $.setval(t, s); return e; } getval(t) { return this.isSurge() || this.isLoon() ? $persistentStore.read(t) : this.isQuanX() ? $prefs.valueForKey(t) : this.isNode() ? ((this.data = this.loaddata()), this.data[t]) : (this.data && this.data[t]) || null; } setval(t, s) { return this.isSurge() || this.isLoon() ? $persistentStore.write(t, s) : this.isQuanX() ? $prefs.setValueForKey(t, s) : this.isNode() ? ((this.data = this.loaddata()), (this.data[s] = t), this.writedata(), !0) : (this.data && this.data[s]) || null; } initGotEnv(t) { (this.got = this.got ? this.got : require('got')), (this.cktough = this.cktough ? this.cktough : require('tough-cookie')), (this.ckjar = this.ckjar ? this.ckjar : new this.cktough.CookieJar()), t && ((t.headers = t.headers ? t.headers : {}), void 0 === t.headers.Cookie && void 0 === t.cookieJar && (t.cookieJar = this.ckjar)); } get(t, s = () => {}) { t.headers && (delete t.headers['Content-Type'], delete t.headers['Content-Length']), this.isSurge() || this.isLoon() ? $httpClient.get(t, (t, e, i) => { !t && e && ((e.body = i), (e.statusCode = e.status)), s(t, e, i); }) : this.isQuanX() ? $task.fetch(t).then( (t) => { const { statusCode: e, statusCode: i, headers: o, body: h } = t; s(null, { status: e, statusCode: i, headers: o, body: h }, h); }, (t) => s(t) ) : this.isNode() && (this.initGotEnv(t), this.got(t) .on('redirect', (t, s) => { try { const e = t.headers['set-cookie'].map(this.cktough.Cookie.parse).toString(); this.ckjar.setCookieSync(e, null), (s.cookieJar = this.ckjar); } catch (t) { this.logErr(t); } }) .then( (t) => { const { statusCode: e, statusCode: i, headers: o, body: h } = t; s(null, { status: e, statusCode: i, headers: o, body: h }, h); }, (t) => s(t) )); } post(t, s = () => {}) { if ( (t.body && t.headers && !t.headers['Content-Type'] && (t.headers['Content-Type'] = 'application/x-www-form-urlencoded'), delete t.headers['Content-Length'], this.isSurge() || this.isLoon()) ) $httpClient.post(t, (t, e, i) => { !t && e && ((e.body = i), (e.statusCode = e.status)), s(t, e, i); }); else if (this.isQuanX()) (t.method = 'POST'), $task.fetch(t).then( (t) => { const { statusCode: e, statusCode: i, headers: o, body: h } = t; s(null, { status: e, statusCode: i, headers: o, body: h }, h); }, (t) => s(t) ); else if (this.isNode()) { this.initGotEnv(t); const { url: e, ...i } = t; this.got.post(e, i).then( (t) => { const { statusCode: e, statusCode: i, headers: o, body: h } = t; s(null, { status: e, statusCode: i, headers: o, body: h }, h); }, (t) => s(t) ); } } time(t) { let s = { 'M+': new Date().getMonth() + 1, 'd+': new Date().getDate(), 'H+': new Date().getHours(), 'm+': new Date().getMinutes(), 's+': new Date().getSeconds(), 'q+': Math.floor((new Date().getMonth() + 3) / 3), S: new Date().getMilliseconds(), }; /(y+)/.test(t) && (t = t.replace(RegExp.$1, (new Date().getFullYear() + '').substr(4 - RegExp.$1.length))); for (let e in s) new RegExp('(' + e + ')').test(t) && (t = t.replace(RegExp.$1, 1 == RegExp.$1.length ? s[e] : ('00' + s[e]).substr(('' + s[e]).length))); return t; } msg(s = t, e = '', i = '', o) { const h = (t) => !t || (!this.isLoon() && this.isSurge()) ? t : 'string' == typeof t ? this.isLoon() ? t : this.isQuanX() ? { 'open-url': t } : void 0 : 'object' == typeof t && (t['open-url'] || t['media-url']) ? this.isLoon() ? t['open-url'] : this.isQuanX() ? t : void 0 : void 0; $.isMute || (this.isSurge() || this.isLoon() ? $notification.post(s, e, i, h(o)) : this.isQuanX() && $notify(s, e, i, h(o))), this.logs.push('', '==============\ud83d\udce3\u7cfb\u7edf\u901a\u77e5\ud83d\udce3=============='), this.logs.push(s), e && this.logs.push(e), i && this.logs.push(i); } log(...t) { t.length > 0 ? (this.logs = [...this.logs, ...t]) : console.log(this.logs.join(this.logSeparator)); } logErr(t, s) { const e = !this.isSurge() && !this.isQuanX() && !this.isLoon(); e ? $.log('', `\u2757\ufe0f${this.name}, \u9519\u8bef!`, t.stack) : $.log('', `\u2757\ufe0f${this.name}, \u9519\u8bef!`, t); } wait(t) { return new Promise((s) => setTimeout(s, t)); } done(t = {}) { const s = new Date().getTime(), e = (s - this.startTime) / 1e3; this.log('', `\ud83d\udd14${this.name}, \u7ed3\u675f! \ud83d\udd5b ${e} \u79d2`), this.log(), (this.isSurge() || this.isQuanX() || this.isLoon()) && $done(t); } })(t, s); }
790 |
--------------------------------------------------------------------------------
/backend/static/assets/index.fa13dcb4.css:
--------------------------------------------------------------------------------
1 | @charset "UTF-8";.el-button{--el-button-font-weight:var(--el-font-weight-primary);--el-button-border-color:var(--el-border-color-base);--el-button-background-color:var(--el-color-white);--el-button-font-color:var(--el-text-color-regular);--el-button-disabled-font-color:var(--el-text-color-placeholder);--el-button-disabled-background-color:var(--el-color-white);--el-button-disabled-border-color:var(--el-border-color-light);--el-button-divide-border-color:rgba(255, 255, 255, .5) }.el-button{display:inline-block;line-height:1;min-height:40px;white-space:nowrap;cursor:pointer;background:var(--el-button-background-color,var(--el-color-white));border:var(--el-border-base);border-color:var(--el-button-border-color,var(--el-border-color-base));color:var(--el-button-font-color,var(--el-text-color-regular));-webkit-appearance:none;text-align:center;-webkit-box-sizing:border-box;box-sizing:border-box;outline:0;margin:0;-webkit-transition:.1s;transition:.1s;font-weight:var(--el-button-font-weight);-moz-user-select:none;-webkit-user-select:none;-ms-user-select:none;padding:12px 20px;font-size:var(--el-font-size-base,14px);border-radius:var(--el-border-radius-base)}.el-button+.el-button{margin-left:10px}.el-button.is-round{padding:12px 20px}.el-button:focus,.el-button:hover{color:var(--el-color-primary);border-color:var(--el-color-primary-light-7);background-color:var(--el-color-primary-light-9);outline:0}.el-button:active{color:#3a8ee6;border-color:#3a8ee6;outline:0}.el-button::-moz-focus-inner{border:0}.el-button [class*=el-icon-]+span{margin-left:5px}.el-button.is-plain:focus,.el-button.is-plain:hover{background:var(--el-color-white);border-color:var(--el-color-primary);color:var(--el-color-primary)}.el-button.is-plain:active{background:var(--el-color-white);border-color:#3a8ee6;color:#3a8ee6;outline:0}.el-button.is-active{color:#3a8ee6;border-color:#3a8ee6}.el-button.is-disabled,.el-button.is-disabled:focus,.el-button.is-disabled:hover{color:var(--el-button-disabled-font-color);cursor:not-allowed;background-image:none;background-color:var(--el-button-disabled-background-color);border-color:var(--el-button-disabled-border-color)}.el-button.is-disabled.el-button--text{background-color:transparent}.el-button.is-disabled.is-plain,.el-button.is-disabled.is-plain:focus,.el-button.is-disabled.is-plain:hover{background-color:var(--el-color-white);border-color:var(--el-button-disabled-border-color);color:var(--el-button-disabled-font-color)}.el-button.is-loading{position:relative;pointer-events:none}.el-button.is-loading:before{pointer-events:none;content:"";position:absolute;left:-1px;top:-1px;right:-1px;bottom:-1px;border-radius:inherit;background-color:#ffffff59}.el-button.is-round{border-radius:var(--el-border-radius-round);padding:12px 23px}.el-button.is-circle{border-radius:50%;padding:12px}.el-button--primary{--el-button-font-color:#ffffff;--el-button-background-color:#409eff;--el-button-border-color:#409eff;--el-button-hover-color:#66b1ff;--el-button-active-font-color:#e6e6e6;--el-button-active-background-color:#0d84ff;--el-button-active-border-color:#0d84ff }.el-button--primary:focus,.el-button--primary:hover{background:var(--el-button-hover-color);border-color:var(--el-button-hover-color);color:var(--el-button-font-color)}.el-button--primary:active{background:var(--el-button-active-background-color);border-color:var(--el-button-active-border-color);color:var(--el-button-active-font-color);outline:0}.el-button--primary.is-active{background:var(--el-button-active-background-color);border-color:var(--el-button-active-border-color);color:var(--el-button-active-font-color)}.el-button--primary.is-disabled,.el-button--primary.is-disabled:active,.el-button--primary.is-disabled:focus,.el-button--primary.is-disabled:hover{color:#fff;background-color:#a0cfff;border-color:#a0cfff}.el-button--primary.is-plain{color:var(--el-button-background-color);background-color:#ecf5ff;border-color:#b3d8ff}.el-button--primary.is-plain:focus,.el-button--primary.is-plain:hover{background:var(--el-button-background-color);border-color:var(--el-button-background-color);color:var(--el-color-white)}.el-button--primary.is-plain:active{background:var(--el-button-active-background-color);border-color:var(--el-button-active-border-color);color:var(--el-color-white);outline:0}.el-button--primary.is-plain.is-disabled,.el-button--primary.is-plain.is-disabled:active,.el-button--primary.is-plain.is-disabled:focus,.el-button--primary.is-plain.is-disabled:hover{color:#8cc5ff;background-color:#ecf5ff;border-color:#d9ecff}.el-button--success{--el-button-font-color:#ffffff;--el-button-background-color:#67c23a;--el-button-border-color:#67c23a;--el-button-hover-color:#85ce61;--el-button-active-font-color:#e6e6e6;--el-button-active-background-color:#529b2e;--el-button-active-border-color:#529b2e }.el-button--success:focus,.el-button--success:hover{background:var(--el-button-hover-color);border-color:var(--el-button-hover-color);color:var(--el-button-font-color)}.el-button--success:active{background:var(--el-button-active-background-color);border-color:var(--el-button-active-border-color);color:var(--el-button-active-font-color);outline:0}.el-button--success.is-active{background:var(--el-button-active-background-color);border-color:var(--el-button-active-border-color);color:var(--el-button-active-font-color)}.el-button--success.is-disabled,.el-button--success.is-disabled:active,.el-button--success.is-disabled:focus,.el-button--success.is-disabled:hover{color:#fff;background-color:#b3e19d;border-color:#b3e19d}.el-button--success.is-plain{color:var(--el-button-background-color);background-color:#f0f9eb;border-color:#c2e7b0}.el-button--success.is-plain:focus,.el-button--success.is-plain:hover{background:var(--el-button-background-color);border-color:var(--el-button-background-color);color:var(--el-color-white)}.el-button--success.is-plain:active{background:var(--el-button-active-background-color);border-color:var(--el-button-active-border-color);color:var(--el-color-white);outline:0}.el-button--success.is-plain.is-disabled,.el-button--success.is-plain.is-disabled:active,.el-button--success.is-plain.is-disabled:focus,.el-button--success.is-plain.is-disabled:hover{color:#a4da89;background-color:#f0f9eb;border-color:#e1f3d8}.el-button--warning{--el-button-font-color:#ffffff;--el-button-background-color:#e6a23c;--el-button-border-color:#e6a23c;--el-button-hover-color:#ebb563;--el-button-active-font-color:#e6e6e6;--el-button-active-background-color:#d48a1b;--el-button-active-border-color:#d48a1b }.el-button--warning:focus,.el-button--warning:hover{background:var(--el-button-hover-color);border-color:var(--el-button-hover-color);color:var(--el-button-font-color)}.el-button--warning:active{background:var(--el-button-active-background-color);border-color:var(--el-button-active-border-color);color:var(--el-button-active-font-color);outline:0}.el-button--warning.is-active{background:var(--el-button-active-background-color);border-color:var(--el-button-active-border-color);color:var(--el-button-active-font-color)}.el-button--warning.is-disabled,.el-button--warning.is-disabled:active,.el-button--warning.is-disabled:focus,.el-button--warning.is-disabled:hover{color:#fff;background-color:#f3d19e;border-color:#f3d19e}.el-button--warning.is-plain{color:var(--el-button-background-color);background-color:#fdf6ec;border-color:#f5dab1}.el-button--warning.is-plain:focus,.el-button--warning.is-plain:hover{background:var(--el-button-background-color);border-color:var(--el-button-background-color);color:var(--el-color-white)}.el-button--warning.is-plain:active{background:var(--el-button-active-background-color);border-color:var(--el-button-active-border-color);color:var(--el-color-white);outline:0}.el-button--warning.is-plain.is-disabled,.el-button--warning.is-plain.is-disabled:active,.el-button--warning.is-plain.is-disabled:focus,.el-button--warning.is-plain.is-disabled:hover{color:#f0c78a;background-color:#fdf6ec;border-color:#faecd8}.el-button--danger{--el-button-font-color:#ffffff;--el-button-background-color:#f56c6c;--el-button-border-color:#f56c6c;--el-button-hover-color:#f78989;--el-button-active-font-color:#e6e6e6;--el-button-active-background-color:#f23c3c;--el-button-active-border-color:#f23c3c }.el-button--danger:focus,.el-button--danger:hover{background:var(--el-button-hover-color);border-color:var(--el-button-hover-color);color:var(--el-button-font-color)}.el-button--danger:active{background:var(--el-button-active-background-color);border-color:var(--el-button-active-border-color);color:var(--el-button-active-font-color);outline:0}.el-button--danger.is-active{background:var(--el-button-active-background-color);border-color:var(--el-button-active-border-color);color:var(--el-button-active-font-color)}.el-button--danger.is-disabled,.el-button--danger.is-disabled:active,.el-button--danger.is-disabled:focus,.el-button--danger.is-disabled:hover{color:#fff;background-color:#fab6b6;border-color:#fab6b6}.el-button--danger.is-plain{color:var(--el-button-background-color);background-color:#fef0f0;border-color:#fbc4c4}.el-button--danger.is-plain:focus,.el-button--danger.is-plain:hover{background:var(--el-button-background-color);border-color:var(--el-button-background-color);color:var(--el-color-white)}.el-button--danger.is-plain:active{background:var(--el-button-active-background-color);border-color:var(--el-button-active-border-color);color:var(--el-color-white);outline:0}.el-button--danger.is-plain.is-disabled,.el-button--danger.is-plain.is-disabled:active,.el-button--danger.is-plain.is-disabled:focus,.el-button--danger.is-plain.is-disabled:hover{color:#f9a7a7;background-color:#fef0f0;border-color:#fde2e2}.el-button--info{--el-button-font-color:#ffffff;--el-button-background-color:#909399;--el-button-border-color:#909399;--el-button-hover-color:#a6a9ad;--el-button-active-font-color:#e6e6e6;--el-button-active-background-color:#767980;--el-button-active-border-color:#767980 }.el-button--info:focus,.el-button--info:hover{background:var(--el-button-hover-color);border-color:var(--el-button-hover-color);color:var(--el-button-font-color)}.el-button--info:active{background:var(--el-button-active-background-color);border-color:var(--el-button-active-border-color);color:var(--el-button-active-font-color);outline:0}.el-button--info.is-active{background:var(--el-button-active-background-color);border-color:var(--el-button-active-border-color);color:var(--el-button-active-font-color)}.el-button--info.is-disabled,.el-button--info.is-disabled:active,.el-button--info.is-disabled:focus,.el-button--info.is-disabled:hover{color:#fff;background-color:#c8c9cc;border-color:#c8c9cc}.el-button--info.is-plain{color:var(--el-button-background-color);background-color:#f4f4f5;border-color:#d3d4d6}.el-button--info.is-plain:focus,.el-button--info.is-plain:hover{background:var(--el-button-background-color);border-color:var(--el-button-background-color);color:var(--el-color-white)}.el-button--info.is-plain:active{background:var(--el-button-active-background-color);border-color:var(--el-button-active-border-color);color:var(--el-color-white);outline:0}.el-button--info.is-plain.is-disabled,.el-button--info.is-plain.is-disabled:active,.el-button--info.is-plain.is-disabled:focus,.el-button--info.is-plain.is-disabled:hover{color:#bcbec2;background-color:#f4f4f5;border-color:#e9e9eb}.el-button--medium{min-height:36px;padding:10px 20px;font-size:var(--el-font-size-base,14px);border-radius:var(--el-border-radius-base)}.el-button--medium.is-round{padding:10px 20px}.el-button--medium.is-circle{padding:10px}.el-button--small{min-height:32px;padding:9px 15px;font-size:12px;border-radius:calc(var(--el-border-radius-base) - 1px)}.el-button--small.is-round{padding:9px 15px}.el-button--small.is-circle{padding:9px}.el-button--mini{min-height:28px;padding:7px 15px;font-size:12px;border-radius:calc(var(--el-border-radius-base) - 1px)}.el-button--mini.is-round{padding:7px 15px}.el-button--mini.is-circle{padding:7px}.el-button--text{border-color:transparent;color:var(--el-color-primary);background:0 0;padding-left:0;padding-right:0}.el-button--text:focus,.el-button--text:hover{color:var(--el-color-primary-light-2);border-color:transparent;background-color:transparent}.el-button--text:active{color:#3a8ee6;border-color:transparent;background-color:transparent}.el-button--text.is-disabled,.el-button--text.is-disabled:focus,.el-button--text.is-disabled:hover{border-color:transparent}.el-textarea{--el-input-font-color:var(--el-text-color-regular);--el-input-border:var(--el-border-base);--el-input-border-color:var(--el-border-color-base);--el-input-border-radius:var(--el-border-radius-base);--el-input-background-color:var(--el-color-white);--el-input-icon-color:var(--el-text-color-placeholder);--el-input-placeholder-color:var(--el-text-color-placeholder);--el-input-hover-border:var(--el-border-color-hover);--el-input-clear-hover-color:var(--el-text-color-secondary);--el-input-focus-border:var(--el-color-primary) }.el-textarea{position:relative;display:inline-block;width:100%;vertical-align:bottom;font-size:var(--el-font-size-base)}.el-textarea__inner{display:block;resize:vertical;padding:5px 15px;line-height:1.5;-webkit-box-sizing:border-box;box-sizing:border-box;width:100%;font-size:inherit;color:var(--el-input-font-color,var(--el-text-color-regular));background-color:var(--el-input-background-color,var(--el-color-white));background-image:none;border:var(--el-input-border,var(--el-border-base));border-radius:var(--el-input-border-radius,var(--el-border-radius-base));-webkit-transition:var(--el-transition-border);transition:var(--el-transition-border)}.el-textarea__inner::-webkit-input-placeholder{color:var(--el-input-placeholder-color,var(--el-text-color-placeholder))}.el-textarea__inner::-moz-placeholder{color:var(--el-input-placeholder-color,var(--el-text-color-placeholder))}.el-textarea__inner:-ms-input-placeholder{color:var(--el-input-placeholder-color,var(--el-text-color-placeholder))}.el-textarea__inner::-ms-input-placeholder{color:var(--el-input-placeholder-color,var(--el-text-color-placeholder))}.el-textarea__inner::placeholder{color:var(--el-input-placeholder-color,var(--el-text-color-placeholder))}.el-textarea__inner:hover{border-color:var(--el-input-hover-border,)}.el-textarea__inner:focus{outline:0;border-color:var(--el-input-focus-border,)}.el-textarea .el-input__count{color:var(--el-color-info);background:var(--el-color-white);position:absolute;font-size:12px;line-height:14px;bottom:5px;right:10px}.el-textarea.is-disabled .el-textarea__inner{background-color:var(--el-disabled-fill-base);border-color:var(--el-disabled-border-base);color:var(--el-disabled-color-base);cursor:not-allowed}.el-textarea.is-disabled .el-textarea__inner::-webkit-input-placeholder{color:var(--el-text-color-placeholder)}.el-textarea.is-disabled .el-textarea__inner::-moz-placeholder{color:var(--el-text-color-placeholder)}.el-textarea.is-disabled .el-textarea__inner:-ms-input-placeholder{color:var(--el-text-color-placeholder)}.el-textarea.is-disabled .el-textarea__inner::-ms-input-placeholder{color:var(--el-text-color-placeholder)}.el-textarea.is-disabled .el-textarea__inner::placeholder{color:var(--el-text-color-placeholder)}.el-textarea.is-exceed .el-textarea__inner{border-color:var(--el-color-danger)}.el-textarea.is-exceed .el-input__count{color:var(--el-color-danger)}.el-input{--el-input-font-color:var(--el-text-color-regular);--el-input-border:var(--el-border-base);--el-input-border-color:var(--el-border-color-base);--el-input-border-radius:var(--el-border-radius-base);--el-input-background-color:var(--el-color-white);--el-input-icon-color:var(--el-text-color-placeholder);--el-input-placeholder-color:var(--el-text-color-placeholder);--el-input-hover-border:var(--el-border-color-hover);--el-input-clear-hover-color:var(--el-text-color-secondary);--el-input-focus-border:var(--el-color-primary);position:relative;font-size:var(--el-font-size-base);display:inline-block;width:100%;line-height:40px}.el-input::-webkit-scrollbar{z-index:11;width:6px}.el-input::-webkit-scrollbar:horizontal{height:6px}.el-input::-webkit-scrollbar-thumb{border-radius:5px;width:6px;background:#b4bccc}.el-input::-webkit-scrollbar-corner{background:#fff}.el-input::-webkit-scrollbar-track{background:#fff}.el-input::-webkit-scrollbar-track-piece{background:#fff;width:6px}.el-input .el-input__clear{color:var(--el-input-icon-color);font-size:var(--el-font-size-base,14px);cursor:pointer;-webkit-transition:var(--el-transition-color);transition:var(--el-transition-color)}.el-input .el-input__clear:hover{color:var(--el-input-clear-hover-color)}.el-input .el-input__count{height:100%;display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;color:var(--el-color-info);font-size:12px}.el-input .el-input__count .el-input__count-inner{background:#fff;line-height:initial;display:inline-block;padding:0 5px}.el-input__inner{-webkit-appearance:none;background-color:var(--el-input-background-color,var(--el-color-white));background-image:none;border-radius:var(--el-input-border-radius,var(--el-border-radius-base));border:var(--el-input-border,var(--el-border-base));-webkit-box-sizing:border-box;box-sizing:border-box;color:var(--el-input-font-color,var(--el-text-color-regular));display:inline-block;font-size:inherit;height:40px;line-height:40px;outline:0;padding:0 15px;-webkit-transition:var(--el-transition-border);transition:var(--el-transition-border);width:100%}.el-input__inner::-webkit-input-placeholder{color:var(--el-input-placeholder-color,var(--el-text-color-placeholder))}.el-input__inner::-moz-placeholder{color:var(--el-input-placeholder-color,var(--el-text-color-placeholder))}.el-input__inner:-ms-input-placeholder{color:var(--el-input-placeholder-color,var(--el-text-color-placeholder))}.el-input__inner::-ms-input-placeholder{color:var(--el-input-placeholder-color,var(--el-text-color-placeholder))}.el-input__inner::placeholder{color:var(--el-input-placeholder-color,var(--el-text-color-placeholder))}.el-input__inner:hover{border-color:var(--el-input-hover-border,var(--el-border-color-hover))}.el-input__inner:focus{outline:0;border-color:var(--el-input-focus-border,var(--el-color-primary))}.el-input__suffix{position:absolute;height:100%;right:5px;top:0;text-align:center;color:var(--el-input-icon-color,var(--el-text-color-placeholder));-webkit-transition:all var(--el-transition-duration);transition:all var(--el-transition-duration);pointer-events:none}.el-input__suffix-inner{pointer-events:all}.el-input__prefix{position:absolute;height:100%;left:5px;top:0;text-align:center;color:var(--el-input-icon-color,var(--el-text-color-placeholder));-webkit-transition:all var(--el-transition-duration);transition:all var(--el-transition-duration)}.el-input__icon{width:25px;text-align:center;-webkit-transition:all var(--el-transition-duration);transition:all var(--el-transition-duration);line-height:40px}.el-input__icon:after{content:"";height:100%;width:0;display:inline-block;vertical-align:middle}.el-input__validateIcon{pointer-events:none}.el-input.is-active .el-input__inner{outline:0;border-color:var(--el-input-focus-border,)}.el-input.is-disabled .el-input__inner{background-color:var(--el-disabled-fill-base);border-color:var(--el-disabled-border-base);color:var(--el-disabled-color-base);cursor:not-allowed}.el-input.is-disabled .el-input__inner::-webkit-input-placeholder{color:var(--el-text-color-placeholder)}.el-input.is-disabled .el-input__inner::-moz-placeholder{color:var(--el-text-color-placeholder)}.el-input.is-disabled .el-input__inner:-ms-input-placeholder{color:var(--el-text-color-placeholder)}.el-input.is-disabled .el-input__inner::-ms-input-placeholder{color:var(--el-text-color-placeholder)}.el-input.is-disabled .el-input__inner::placeholder{color:var(--el-text-color-placeholder)}.el-input.is-disabled .el-input__icon{cursor:not-allowed}.el-input.is-exceed .el-input__inner{border-color:var(--el-color-danger)}.el-input.is-exceed .el-input__suffix .el-input__count{color:var(--el-color-danger)}.el-input--suffix .el-input__inner{padding-right:30px}.el-input--suffix--password-clear .el-input__inner{padding-right:55px}.el-input--prefix .el-input__inner{padding-left:30px}.el-input--medium{font-size:14px;line-height:36px}.el-input--medium .el-input__inner{height:36px;line-height:36px}.el-input--medium .el-input__icon{line-height:36px}.el-input--small{font-size:13px;line-height:32px}.el-input--small .el-input__inner{height:32px;line-height:32px}.el-input--small .el-input__icon{line-height:32px}.el-input--mini{font-size:12px;line-height:28px}.el-input--mini .el-input__inner{height:28px;line-height:28px}.el-input--mini .el-input__icon{line-height:28px}.el-input-group{line-height:normal;display:inline-table;width:100%;border-collapse:separate;border-spacing:0}.el-input-group>.el-input__inner{vertical-align:middle;display:table-cell}.el-input-group__append,.el-input-group__prepend{background-color:var(--el-background-color-base);color:var(--el-color-info);vertical-align:middle;display:table-cell;position:relative;border:1px solid #dcdfe6;border-radius:var(--el-input-border-radius);padding:0 20px;width:1px;white-space:nowrap}.el-input-group__append:focus,.el-input-group__prepend:focus{outline:0}.el-input-group__append .el-button,.el-input-group__append .el-select,.el-input-group__prepend .el-button,.el-input-group__prepend .el-select{display:inline-block;margin:-10px -20px}.el-input-group__append button.el-button,.el-input-group__append div.el-select .el-input__inner,.el-input-group__append div.el-select:hover .el-input__inner,.el-input-group__prepend button.el-button,.el-input-group__prepend div.el-select .el-input__inner,.el-input-group__prepend div.el-select:hover .el-input__inner{border-color:transparent;background-color:transparent;color:inherit;border-top:0;border-bottom:0}.el-input-group__append .el-button,.el-input-group__append .el-input,.el-input-group__prepend .el-button,.el-input-group__prepend .el-input{font-size:inherit}.el-input-group__prepend{border-right:0;border-top-right-radius:0;border-bottom-right-radius:0}.el-input-group__append{border-left:0;border-top-left-radius:0;border-bottom-left-radius:0}.el-input-group--prepend .el-input__inner{border-top-left-radius:0;border-bottom-left-radius:0}.el-input-group--prepend .el-select .el-input.is-focus .el-input__inner{border-color:transparent}.el-input-group--append .el-input__inner{border-top-right-radius:0;border-bottom-right-radius:0}.el-input-group--append .el-select .el-input.is-focus .el-input__inner{border-color:transparent}.el-input__inner::-ms-clear{display:none;width:0;height:0}.el-message{--el-message-min-width:380px;--el-message-background-color:#edf2fc;--el-message-padding:15px 15px 15px 20px;--el-message-close-size:16px;--el-message-close-icon-color:var(--el-text-color-placeholder);--el-message-close-hover-color:var(--el-text-color-secondary) }.el-message{min-width:var(--el-message-min-width);-webkit-box-sizing:border-box;box-sizing:border-box;border-radius:var(--el-border-radius-base);border-width:var(--el-border-width-base);border-style:var(--el-border-style-base);border-color:var(--el-border-color-lighter);position:fixed;left:50%;top:20px;-webkit-transform:translateX(-50%);transform:translate(-50%);-webkit-transition:opacity .3s,top .4s,-webkit-transform .4s;transition:opacity .3s,top .4s,-webkit-transform .4s;transition:opacity .3s,transform .4s,top .4s;transition:opacity .3s,transform .4s,top .4s,-webkit-transform .4s;background-color:var(--el-message-background-color);-webkit-transition:opacity var(--el-transition-duration),top .4s,-webkit-transform .4s;transition:opacity var(--el-transition-duration),top .4s,-webkit-transform .4s;transition:opacity var(--el-transition-duration),transform .4s,top .4s;transition:opacity var(--el-transition-duration),transform .4s,top .4s,-webkit-transform .4s;overflow:hidden;padding:var(--el-message-padding);display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.el-message.is-center{-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center}.el-message.is-closable .el-message__content{padding-right:16px}.el-message p{margin:0}.el-message--info .el-message__content{color:var(--el-message-info-font-color)}.el-message--success{background-color:#f0f9eb;border-color:#e1f3d8;--el-message-font-color:var(--el-color-success) }.el-message--success .el-message__content{color:var(--el-message-font-color)}.el-message--info{background-color:#f4f4f5;border-color:#e9e9eb;--el-message-font-color:var(--el-color-info) }.el-message--info .el-message__content{color:var(--el-message-font-color)}.el-message--warning{background-color:#fdf6ec;border-color:#faecd8;--el-message-font-color:var(--el-color-warning) }.el-message--warning .el-message__content{color:var(--el-message-font-color)}.el-message--error{background-color:#fef0f0;border-color:#fde2e2;--el-message-font-color:var(--el-color-error) }.el-message--error .el-message__content{color:var(--el-message-font-color)}.el-message__icon{margin-right:10px}.el-message__content{padding:0;font-size:14px;line-height:1}.el-message__content:focus{outline-width:0}.el-message__closeBtn{position:absolute;top:50%;right:15px;-webkit-transform:translateY(-50%);transform:translateY(-50%);cursor:pointer;color:var(--el-message-close-icon-color);font-size:var(--el-message-close-size,16px)}.el-message__closeBtn:focus{outline-width:0}.el-message__closeBtn:hover{color:var(--el-message-close-hover-color)}.el-message .el-icon-success{--el-message-font-color:var(--el-color-success);color:var(--el-message-font-color)}.el-message .el-icon-info{--el-message-font-color:var(--el-color-info);color:var(--el-message-font-color)}.el-message .el-icon-warning{--el-message-font-color:var(--el-color-warning);color:var(--el-message-font-color)}.el-message .el-icon-error{--el-message-font-color:var(--el-color-error);color:var(--el-message-font-color)}.el-message-fade-enter-from,.el-message-fade-leave-to{opacity:0;-webkit-transform:translate(-50%,-100%);transform:translate(-50%,-100%)}:root{--el-color-white:#ffffff;--el-color-black:#000000;--el-color-primary:#409eff;--el-color-primary-light-1:#53a8ff;--el-color-primary-light-2:#66b1ff;--el-color-primary-light-3:#79bbff;--el-color-primary-light-4:#8cc5ff;--el-color-primary-light-5:#a0cfff;--el-color-primary-light-6:#b3d8ff;--el-color-primary-light-7:#c6e2ff;--el-color-primary-light-8:#d9ecff;--el-color-primary-light-9:#ecf5ff;--el-color-success:#67c23a;--el-color-success-light:#e1f3d8;--el-color-success-lighter:#f0f9eb;--el-color-warning:#e6a23c;--el-color-warning-light:#faecd8;--el-color-warning-lighter:#fdf6ec;--el-color-danger:#f56c6c;--el-color-danger-light:#fde2e2;--el-color-danger-lighter:#fef0f0;--el-color-error:#f56c6c;--el-color-error-light:#fde2e2;--el-color-error-lighter:#fef0f0;--el-color-info:#909399;--el-color-info-light:#e9e9eb;--el-color-info-lighter:#f4f4f5;--el-text-color-primary:#303133;--el-text-color-regular:#606266;--el-text-color-secondary:#909399;--el-text-color-placeholder:#c0c4cc;--el-border-color-base:#dcdfe6;--el-border-color-light:#e4e7ed;--el-border-color-lighter:#ebeef5;--el-border-color-extra-light:#f2f6fc;--el-background-color-base:#f5f7fa;--el-border-width-base:1px;--el-border-style-base:solid;--el-border-color-hover:var(--el-text-color-placeholder);--el-border-base:var(--el-border-width-base) var(--el-border-style-base) var(--el-border-color-base);--el-border-radius-base:4px;--el-border-radius-small:2px;--el-border-radius-round:20px;--el-border-radius-circle:100%;--el-box-shadow-base:0 2px 4px rgba(0, 0, 0, .12),0 0 6px rgba(0, 0, 0, .04);--el-box-shadow-light:0 2px 12px 0 rgba(0, 0, 0, .1);--el-svg-monochrome-grey:#dcdde0;--el-fill-base:var(--el-color-white);--el-font-size-extra-large:20px;--el-font-size-large:18px;--el-font-size-medium:16px;--el-font-size-base:14px;--el-font-size-small:13px;--el-font-size-extra-small:12px;--el-font-weight-primary:500;--el-font-line-height-primary:24px;--el-font-color-disabled-base:#bbb;--el-index-normal:1;--el-index-top:1000;--el-index-popper:2000;--el-disabled-fill-base:var(--el-background-color-base);--el-disabled-color-base:var(--el-text-color-placeholder);--el-disabled-border-base:var(--el-border-color-light);--el-transition-duration:.3s;--el-transition-duration-fast:.2s;--el-transition-function-ease-in-out-bezier:cubic-bezier(.645, .045, .355, 1);--el-transition-function-fast-bezier:cubic-bezier(.23, 1, .32, 1);--el-transition-all:all var(--el-transition-duration) var(--el-transition-function-ease-in-out-bezier);--el-transition-fade:opacity var(--el-transition-duration) var(--el-transition-function-fast-bezier);--el-transition-md-fade:transform var(--el-transition-duration) var(--el-transition-function-fast-bezier),opacity var(--el-transition-duration) var(--el-transition-function-fast-bezier);--el-transition-fade-linear:opacity var(--el-transition-duration-fast) linear;--el-transition-border:border-color var(--el-transition-duration-fast) var(--el-transition-function-ease-in-out-bezier);--el-transition-color:color var(--el-transition-duration-fast) var(--el-transition-function-ease-in-out-bezier) }.fade-in-linear-enter-active,.fade-in-linear-leave-active{-webkit-transition:var(--el-transition-fade-linear);transition:var(--el-transition-fade-linear)}.fade-in-linear-enter-from,.fade-in-linear-leave-to{opacity:0}.el-fade-in-linear-enter-active,.el-fade-in-linear-leave-active{-webkit-transition:var(--el-transition-fade-linear);transition:var(--el-transition-fade-linear)}.el-fade-in-linear-enter-from,.el-fade-in-linear-leave-to{opacity:0}.el-fade-in-enter-active,.el-fade-in-leave-active{-webkit-transition:all var(--el-transition-duration) cubic-bezier(.55,0,.1,1);transition:all var(--el-transition-duration) cubic-bezier(.55,0,.1,1)}.el-fade-in-enter-from,.el-fade-in-leave-active{opacity:0}.el-zoom-in-center-enter-active,.el-zoom-in-center-leave-active{-webkit-transition:all var(--el-transition-duration) cubic-bezier(.55,0,.1,1);transition:all var(--el-transition-duration) cubic-bezier(.55,0,.1,1)}.el-zoom-in-center-enter-from,.el-zoom-in-center-leave-active{opacity:0;-webkit-transform:scaleX(0);transform:scaleX(0)}.el-zoom-in-top-enter-active,.el-zoom-in-top-leave-active{opacity:1;-webkit-transform:scaleY(1);transform:scaleY(1);-webkit-transition:var(--el-transition-md-fade);transition:var(--el-transition-md-fade);-webkit-transform-origin:center top;transform-origin:center top}.el-zoom-in-top-enter-active[data-popper-placement^=top],.el-zoom-in-top-leave-active[data-popper-placement^=top]{-webkit-transform-origin:center bottom;transform-origin:center bottom}.el-zoom-in-top-enter-from,.el-zoom-in-top-leave-active{opacity:0;-webkit-transform:scaleY(0);transform:scaleY(0)}.el-zoom-in-bottom-enter-active,.el-zoom-in-bottom-leave-active{opacity:1;-webkit-transform:scaleY(1);transform:scaleY(1);-webkit-transition:var(--el-transition-md-fade);transition:var(--el-transition-md-fade);-webkit-transform-origin:center bottom;transform-origin:center bottom}.el-zoom-in-bottom-enter-from,.el-zoom-in-bottom-leave-active{opacity:0;-webkit-transform:scaleY(0);transform:scaleY(0)}.el-zoom-in-left-enter-active,.el-zoom-in-left-leave-active{opacity:1;-webkit-transform:scale(1,1);transform:scale(1);-webkit-transition:var(--el-transition-md-fade);transition:var(--el-transition-md-fade);-webkit-transform-origin:top left;transform-origin:top left}.el-zoom-in-left-enter-from,.el-zoom-in-left-leave-active{opacity:0;-webkit-transform:scale(.45,.45);transform:scale(.45)}.collapse-transition{-webkit-transition:var(--el-transition-duration) height ease-in-out,var(--el-transition-duration) padding-top ease-in-out,var(--el-transition-duration) padding-bottom ease-in-out;transition:var(--el-transition-duration) height ease-in-out,var(--el-transition-duration) padding-top ease-in-out,var(--el-transition-duration) padding-bottom ease-in-out}.horizontal-collapse-transition{-webkit-transition:var(--el-transition-duration) width ease-in-out,var(--el-transition-duration) padding-left ease-in-out,var(--el-transition-duration) padding-right ease-in-out;transition:var(--el-transition-duration) width ease-in-out,var(--el-transition-duration) padding-left ease-in-out,var(--el-transition-duration) padding-right ease-in-out}.el-list-enter-active,.el-list-leave-active{-webkit-transition:all 1s;transition:all 1s}.el-list-enter-from,.el-list-leave-active{opacity:0;-webkit-transform:translateY(-30px);transform:translateY(-30px)}.el-opacity-transition{-webkit-transition:opacity var(--el-transition-duration) cubic-bezier(.55,0,.1,1);transition:opacity var(--el-transition-duration) cubic-bezier(.55,0,.1,1)}@font-face{font-family:element-icons;src:url(/assets/element-icons.9c88a535.woff) format("woff"),url(/assets/element-icons.de5eb258.ttf) format("truetype");font-weight:400;font-display:auto;font-style:normal}[class*=" el-icon-"],[class^=el-icon-]{font-family:element-icons!important;speak:none;font-style:normal;font-weight:400;font-variant:normal;text-transform:none;line-height:1;vertical-align:baseline;display:inline-block;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.el-icon-ice-cream-round:before{content:"\e6a0"}.el-icon-ice-cream-square:before{content:"\e6a3"}.el-icon-lollipop:before{content:"\e6a4"}.el-icon-potato-strips:before{content:"\e6a5"}.el-icon-milk-tea:before{content:"\e6a6"}.el-icon-ice-drink:before{content:"\e6a7"}.el-icon-ice-tea:before{content:"\e6a9"}.el-icon-coffee:before{content:"\e6aa"}.el-icon-orange:before{content:"\e6ab"}.el-icon-pear:before{content:"\e6ac"}.el-icon-apple:before{content:"\e6ad"}.el-icon-cherry:before{content:"\e6ae"}.el-icon-watermelon:before{content:"\e6af"}.el-icon-grape:before{content:"\e6b0"}.el-icon-refrigerator:before{content:"\e6b1"}.el-icon-goblet-square-full:before{content:"\e6b2"}.el-icon-goblet-square:before{content:"\e6b3"}.el-icon-goblet-full:before{content:"\e6b4"}.el-icon-goblet:before{content:"\e6b5"}.el-icon-cold-drink:before{content:"\e6b6"}.el-icon-coffee-cup:before{content:"\e6b8"}.el-icon-water-cup:before{content:"\e6b9"}.el-icon-hot-water:before{content:"\e6ba"}.el-icon-ice-cream:before{content:"\e6bb"}.el-icon-dessert:before{content:"\e6bc"}.el-icon-sugar:before{content:"\e6bd"}.el-icon-tableware:before{content:"\e6be"}.el-icon-burger:before{content:"\e6bf"}.el-icon-knife-fork:before{content:"\e6c1"}.el-icon-fork-spoon:before{content:"\e6c2"}.el-icon-chicken:before{content:"\e6c3"}.el-icon-food:before{content:"\e6c4"}.el-icon-dish-1:before{content:"\e6c5"}.el-icon-dish:before{content:"\e6c6"}.el-icon-moon-night:before{content:"\e6ee"}.el-icon-moon:before{content:"\e6f0"}.el-icon-cloudy-and-sunny:before{content:"\e6f1"}.el-icon-partly-cloudy:before{content:"\e6f2"}.el-icon-cloudy:before{content:"\e6f3"}.el-icon-sunny:before{content:"\e6f6"}.el-icon-sunset:before{content:"\e6f7"}.el-icon-sunrise-1:before{content:"\e6f8"}.el-icon-sunrise:before{content:"\e6f9"}.el-icon-heavy-rain:before{content:"\e6fa"}.el-icon-lightning:before{content:"\e6fb"}.el-icon-light-rain:before{content:"\e6fc"}.el-icon-wind-power:before{content:"\e6fd"}.el-icon-baseball:before{content:"\e712"}.el-icon-soccer:before{content:"\e713"}.el-icon-football:before{content:"\e715"}.el-icon-basketball:before{content:"\e716"}.el-icon-ship:before{content:"\e73f"}.el-icon-truck:before{content:"\e740"}.el-icon-bicycle:before{content:"\e741"}.el-icon-mobile-phone:before{content:"\e6d3"}.el-icon-service:before{content:"\e6d4"}.el-icon-key:before{content:"\e6e2"}.el-icon-unlock:before{content:"\e6e4"}.el-icon-lock:before{content:"\e6e5"}.el-icon-watch:before{content:"\e6fe"}.el-icon-watch-1:before{content:"\e6ff"}.el-icon-timer:before{content:"\e702"}.el-icon-alarm-clock:before{content:"\e703"}.el-icon-map-location:before{content:"\e704"}.el-icon-delete-location:before{content:"\e705"}.el-icon-add-location:before{content:"\e706"}.el-icon-location-information:before{content:"\e707"}.el-icon-location-outline:before{content:"\e708"}.el-icon-location:before{content:"\e79e"}.el-icon-place:before{content:"\e709"}.el-icon-discover:before{content:"\e70a"}.el-icon-first-aid-kit:before{content:"\e70b"}.el-icon-trophy-1:before{content:"\e70c"}.el-icon-trophy:before{content:"\e70d"}.el-icon-medal:before{content:"\e70e"}.el-icon-medal-1:before{content:"\e70f"}.el-icon-stopwatch:before{content:"\e710"}.el-icon-mic:before{content:"\e711"}.el-icon-copy-document:before{content:"\e718"}.el-icon-full-screen:before{content:"\e719"}.el-icon-switch-button:before{content:"\e71b"}.el-icon-aim:before{content:"\e71c"}.el-icon-crop:before{content:"\e71d"}.el-icon-odometer:before{content:"\e71e"}.el-icon-time:before{content:"\e71f"}.el-icon-bangzhu:before{content:"\e724"}.el-icon-close-notification:before{content:"\e726"}.el-icon-microphone:before{content:"\e727"}.el-icon-turn-off-microphone:before{content:"\e728"}.el-icon-position:before{content:"\e729"}.el-icon-postcard:before{content:"\e72a"}.el-icon-message:before{content:"\e72b"}.el-icon-chat-line-square:before{content:"\e72d"}.el-icon-chat-dot-square:before{content:"\e72e"}.el-icon-chat-dot-round:before{content:"\e72f"}.el-icon-chat-square:before{content:"\e730"}.el-icon-chat-line-round:before{content:"\e731"}.el-icon-chat-round:before{content:"\e732"}.el-icon-set-up:before{content:"\e733"}.el-icon-turn-off:before{content:"\e734"}.el-icon-open:before{content:"\e735"}.el-icon-connection:before{content:"\e736"}.el-icon-link:before{content:"\e737"}.el-icon-cpu:before{content:"\e738"}.el-icon-thumb:before{content:"\e739"}.el-icon-female:before{content:"\e73a"}.el-icon-male:before{content:"\e73b"}.el-icon-guide:before{content:"\e73c"}.el-icon-news:before{content:"\e73e"}.el-icon-price-tag:before{content:"\e744"}.el-icon-discount:before{content:"\e745"}.el-icon-wallet:before{content:"\e747"}.el-icon-coin:before{content:"\e748"}.el-icon-money:before{content:"\e749"}.el-icon-bank-card:before{content:"\e74a"}.el-icon-box:before{content:"\e74b"}.el-icon-present:before{content:"\e74c"}.el-icon-sell:before{content:"\e6d5"}.el-icon-sold-out:before{content:"\e6d6"}.el-icon-shopping-bag-2:before{content:"\e74d"}.el-icon-shopping-bag-1:before{content:"\e74e"}.el-icon-shopping-cart-2:before{content:"\e74f"}.el-icon-shopping-cart-1:before{content:"\e750"}.el-icon-shopping-cart-full:before{content:"\e751"}.el-icon-smoking:before{content:"\e752"}.el-icon-no-smoking:before{content:"\e753"}.el-icon-house:before{content:"\e754"}.el-icon-table-lamp:before{content:"\e755"}.el-icon-school:before{content:"\e756"}.el-icon-office-building:before{content:"\e757"}.el-icon-toilet-paper:before{content:"\e758"}.el-icon-notebook-2:before{content:"\e759"}.el-icon-notebook-1:before{content:"\e75a"}.el-icon-files:before{content:"\e75b"}.el-icon-collection:before{content:"\e75c"}.el-icon-receiving:before{content:"\e75d"}.el-icon-suitcase-1:before{content:"\e760"}.el-icon-suitcase:before{content:"\e761"}.el-icon-film:before{content:"\e763"}.el-icon-collection-tag:before{content:"\e765"}.el-icon-data-analysis:before{content:"\e766"}.el-icon-pie-chart:before{content:"\e767"}.el-icon-data-board:before{content:"\e768"}.el-icon-data-line:before{content:"\e76d"}.el-icon-reading:before{content:"\e769"}.el-icon-magic-stick:before{content:"\e76a"}.el-icon-coordinate:before{content:"\e76b"}.el-icon-mouse:before{content:"\e76c"}.el-icon-brush:before{content:"\e76e"}.el-icon-headset:before{content:"\e76f"}.el-icon-umbrella:before{content:"\e770"}.el-icon-scissors:before{content:"\e771"}.el-icon-mobile:before{content:"\e773"}.el-icon-attract:before{content:"\e774"}.el-icon-monitor:before{content:"\e775"}.el-icon-search:before{content:"\e778"}.el-icon-takeaway-box:before{content:"\e77a"}.el-icon-paperclip:before{content:"\e77d"}.el-icon-printer:before{content:"\e77e"}.el-icon-document-add:before{content:"\e782"}.el-icon-document:before{content:"\e785"}.el-icon-document-checked:before{content:"\e786"}.el-icon-document-copy:before{content:"\e787"}.el-icon-document-delete:before{content:"\e788"}.el-icon-document-remove:before{content:"\e789"}.el-icon-tickets:before{content:"\e78b"}.el-icon-folder-checked:before{content:"\e77f"}.el-icon-folder-delete:before{content:"\e780"}.el-icon-folder-remove:before{content:"\e781"}.el-icon-folder-add:before{content:"\e783"}.el-icon-folder-opened:before{content:"\e784"}.el-icon-folder:before{content:"\e78a"}.el-icon-edit-outline:before{content:"\e764"}.el-icon-edit:before{content:"\e78c"}.el-icon-date:before{content:"\e78e"}.el-icon-c-scale-to-original:before{content:"\e7c6"}.el-icon-view:before{content:"\e6ce"}.el-icon-loading:before{content:"\e6cf"}.el-icon-rank:before{content:"\e6d1"}.el-icon-sort-down:before{content:"\e7c4"}.el-icon-sort-up:before{content:"\e7c5"}.el-icon-sort:before{content:"\e6d2"}.el-icon-finished:before{content:"\e6cd"}.el-icon-refresh-left:before{content:"\e6c7"}.el-icon-refresh-right:before{content:"\e6c8"}.el-icon-refresh:before{content:"\e6d0"}.el-icon-video-play:before{content:"\e7c0"}.el-icon-video-pause:before{content:"\e7c1"}.el-icon-d-arrow-right:before{content:"\e6dc"}.el-icon-d-arrow-left:before{content:"\e6dd"}.el-icon-arrow-up:before{content:"\e6e1"}.el-icon-arrow-down:before{content:"\e6df"}.el-icon-arrow-right:before{content:"\e6e0"}.el-icon-arrow-left:before{content:"\e6de"}.el-icon-top-right:before{content:"\e6e7"}.el-icon-top-left:before{content:"\e6e8"}.el-icon-top:before{content:"\e6e6"}.el-icon-bottom:before{content:"\e6eb"}.el-icon-right:before{content:"\e6e9"}.el-icon-back:before{content:"\e6ea"}.el-icon-bottom-right:before{content:"\e6ec"}.el-icon-bottom-left:before{content:"\e6ed"}.el-icon-caret-top:before{content:"\e78f"}.el-icon-caret-bottom:before{content:"\e790"}.el-icon-caret-right:before{content:"\e791"}.el-icon-caret-left:before{content:"\e792"}.el-icon-d-caret:before{content:"\e79a"}.el-icon-share:before{content:"\e793"}.el-icon-menu:before{content:"\e798"}.el-icon-s-grid:before{content:"\e7a6"}.el-icon-s-check:before{content:"\e7a7"}.el-icon-s-data:before{content:"\e7a8"}.el-icon-s-opportunity:before{content:"\e7aa"}.el-icon-s-custom:before{content:"\e7ab"}.el-icon-s-claim:before{content:"\e7ad"}.el-icon-s-finance:before{content:"\e7ae"}.el-icon-s-comment:before{content:"\e7af"}.el-icon-s-flag:before{content:"\e7b0"}.el-icon-s-marketing:before{content:"\e7b1"}.el-icon-s-shop:before{content:"\e7b4"}.el-icon-s-open:before{content:"\e7b5"}.el-icon-s-management:before{content:"\e7b6"}.el-icon-s-ticket:before{content:"\e7b7"}.el-icon-s-release:before{content:"\e7b8"}.el-icon-s-home:before{content:"\e7b9"}.el-icon-s-promotion:before{content:"\e7ba"}.el-icon-s-operation:before{content:"\e7bb"}.el-icon-s-unfold:before{content:"\e7bc"}.el-icon-s-fold:before{content:"\e7a9"}.el-icon-s-platform:before{content:"\e7bd"}.el-icon-s-order:before{content:"\e7be"}.el-icon-s-cooperation:before{content:"\e7bf"}.el-icon-bell:before{content:"\e725"}.el-icon-message-solid:before{content:"\e799"}.el-icon-video-camera:before{content:"\e772"}.el-icon-video-camera-solid:before{content:"\e796"}.el-icon-camera:before{content:"\e779"}.el-icon-camera-solid:before{content:"\e79b"}.el-icon-download:before{content:"\e77c"}.el-icon-upload2:before{content:"\e77b"}.el-icon-upload:before{content:"\e7c3"}.el-icon-picture-outline-round:before{content:"\e75f"}.el-icon-picture-outline:before{content:"\e75e"}.el-icon-picture:before{content:"\e79f"}.el-icon-close:before{content:"\e6db"}.el-icon-check:before{content:"\e6da"}.el-icon-plus:before{content:"\e6d9"}.el-icon-minus:before{content:"\e6d8"}.el-icon-help:before{content:"\e73d"}.el-icon-s-help:before{content:"\e7b3"}.el-icon-circle-close:before{content:"\e78d"}.el-icon-circle-check:before{content:"\e720"}.el-icon-circle-plus-outline:before{content:"\e723"}.el-icon-remove-outline:before{content:"\e722"}.el-icon-zoom-out:before{content:"\e776"}.el-icon-zoom-in:before{content:"\e777"}.el-icon-error:before{content:"\e79d"}.el-icon-success:before{content:"\e79c"}.el-icon-circle-plus:before{content:"\e7a0"}.el-icon-remove:before{content:"\e7a2"}.el-icon-info:before{content:"\e7a1"}.el-icon-question:before{content:"\e7a4"}.el-icon-warning-outline:before{content:"\e6c9"}.el-icon-warning:before{content:"\e7a3"}.el-icon-goods:before{content:"\e7c2"}.el-icon-s-goods:before{content:"\e7b2"}.el-icon-star-off:before{content:"\e717"}.el-icon-star-on:before{content:"\e797"}.el-icon-more-outline:before{content:"\e6cc"}.el-icon-more:before{content:"\e794"}.el-icon-phone-outline:before{content:"\e6cb"}.el-icon-phone:before{content:"\e795"}.el-icon-user:before{content:"\e6e3"}.el-icon-user-solid:before{content:"\e7a5"}.el-icon-setting:before{content:"\e6ca"}.el-icon-s-tools:before{content:"\e7ac"}.el-icon-delete:before{content:"\e6d7"}.el-icon-delete-solid:before{content:"\e7c9"}.el-icon-eleme:before{content:"\e7c7"}.el-icon-platform-eleme:before{content:"\e7ca"}.el-icon-loading{-webkit-animation:rotating 2s linear infinite;animation:rotating 2s linear infinite}.el-icon--right{margin-left:5px}.el-icon--left{margin-right:5px}@-webkit-keyframes rotating{0%{-webkit-transform:rotateZ(0);transform:rotate(0)}to{-webkit-transform:rotateZ(360deg);transform:rotate(360deg)}}@keyframes rotating{0%{-webkit-transform:rotateZ(0);transform:rotate(0)}to{-webkit-transform:rotateZ(360deg);transform:rotate(360deg)}}.el-icon{--color:inherit;--font-size:14px;height:1em;width:1em;line-height:1em;text-align:center;display:inline-block;position:relative;fill:currentColor;color:var(--color);font-size:var(--font-size)}.el-icon.is-loading{-webkit-animation:rotating 2s linear infinite;animation:rotating 2s linear infinite}.el-icon svg{height:1em;width:1em}a[data-v-4b23e37a]{color:#42b983}.NinjaLogo{-webkit-animation:1s appear;animation:1s appear}@-webkit-keyframes appear{0%{opacity:0}}@keyframes appear{0%{opacity:0}}.header[data-v-1f23ce5f]{height:4rem;--tw-bg-opacity: 1;background-color:rgba(255,255,255,var(--tw-bg-opacity));--tw-shadow: 0 4px 6px -1px rgba(0, 0, 0, .1), 0 2px 4px -1px rgba(0, 0, 0, .06);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow);--tw-drop-shadow: drop-shadow(0 1px 1px rgba(0,0,0,.05))}.header-wrapper[data-v-1f23ce5f]{margin:auto;display:flex;height:100%;width:91.666667%;max-width:64rem;align-items:center;justify-content:space-between;font-size:1.5rem;line-height:2rem;font-weight:700}#app{font-family:"Source Sans Pro",-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;font-size:16px;word-spacing:1px;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;box-sizing:border-box;min-height:100vh;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;--tw-bg-opacity: 1;background-color:rgba(249,250,251,var(--tw-bg-opacity));padding-bottom:1.25rem}*,*:before,*:after{box-sizing:border-box;margin:0}.content{margin:auto;width:91.666667%;max-width:64rem}.card{margin:1.25rem auto auto;border-radius:.5rem;--tw-bg-opacity: 1;background-color:rgba(255,255,255,var(--tw-bg-opacity));--tw-shadow: 0 10px 15px -3px rgba(0, 0, 0, .1), 0 4px 6px -2px rgba(0, 0, 0, .05);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.card-header{border-bottom-width:1px;padding:1rem}.card-title{font-size:1.125rem;line-height:1.75rem;font-weight:500}.card-subtitle{font-size:.875rem;line-height:1.25rem;font-weight:400;--tw-text-opacity: 1;color:rgba(107,114,128,var(--tw-text-opacity))}.card-body{padding:1rem;font-size:.875rem;line-height:1.25rem}.card-footer{padding-right:1rem;padding-bottom:1rem;padding-left:1rem;text-align:right}*,:before,:after{box-sizing:border-box}html{-moz-tab-size:4;-o-tab-size:4;tab-size:4}html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}body{font-family:system-ui,-apple-system,"Segoe UI",Roboto,Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji"}hr{height:0;color:inherit}abbr[title]{-webkit-text-decoration:underline dotted;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:ui-monospace,SFMono-Regular,Consolas,"Liberation Mono",Menlo,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,select{text-transform:none}button,[type=button]{-webkit-appearance:button}legend{padding:0}progress{vertical-align:baseline}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}button{background-color:transparent;background-image:none}fieldset{margin:0;padding:0}ol,ul{list-style:none;margin:0;padding:0}html{font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";line-height:1.5}body{font-family:inherit;line-height:inherit}*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:currentColor}hr{border-top-width:1px}img{border-style:solid}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input:-ms-input-placeholder,textarea:-ms-input-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button{cursor:pointer}table{border-collapse:collapse}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}button,input,optgroup,select,textarea{padding:0;line-height:inherit;color:inherit}pre,code,kbd,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}*,:before,:after{--tw-border-opacity: 1;border-color:rgba(229,231,235,var(--tw-border-opacity))}#app .absolute{position:absolute}#app .z-50{z-index:50}#app .m-auto{margin:auto}#app .my-4{margin-top:1rem;margin-bottom:1rem}#app .mt-4{margin-top:1rem}#app .mt-5{margin-top:1.25rem}#app .ml-0{margin-left:0}#app .ml-2{margin-left:.5rem}#app .flex{display:flex}#app .table{display:table}#app .h-10{height:2.5rem}#app .h-16{height:4rem}#app .h-full{height:100%}#app .h-screen{height:100vh}#app .min-h-screen{min-height:100vh}#app .w-10{width:2.5rem}#app .w-48{width:12rem}#app .w-11\/12{width:91.666667%}#app .w-full{width:100%}#app .w-screen{width:100vw}#app .max-w-5xl{max-width:64rem}@-webkit-keyframes spin{to{transform:rotate(360deg)}}@keyframes spin{to{transform:rotate(360deg)}}@-webkit-keyframes ping{75%,to{transform:scale(2);opacity:0}}@keyframes ping{75%,to{transform:scale(2);opacity:0}}@-webkit-keyframes pulse{50%{opacity:.5}}@keyframes pulse{50%{opacity:.5}}@-webkit-keyframes bounce{0%,to{transform:translateY(-25%);-webkit-animation-timing-function:cubic-bezier(.8,0,1,1);animation-timing-function:cubic-bezier(.8,0,1,1)}50%{transform:none;-webkit-animation-timing-function:cubic-bezier(0,0,.2,1);animation-timing-function:cubic-bezier(0,0,.2,1)}}@keyframes bounce{0%,to{transform:translateY(-25%);-webkit-animation-timing-function:cubic-bezier(.8,0,1,1);animation-timing-function:cubic-bezier(.8,0,1,1)}50%{transform:none;-webkit-animation-timing-function:cubic-bezier(0,0,.2,1);animation-timing-function:cubic-bezier(0,0,.2,1)}}#app .select-none{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}#app .flex-col{flex-direction:column}#app .items-center{align-items:center}#app .justify-center{justify-content:center}#app .justify-between{justify-content:space-between}#app .rounded-lg{border-radius:.5rem}#app .rounded-full{border-radius:9999px}#app .border-b{border-bottom-width:1px}#app .bg-black{--tw-bg-opacity: 1;background-color:rgba(0,0,0,var(--tw-bg-opacity))}#app .bg-gray-50{--tw-bg-opacity: 1;background-color:rgba(249,250,251,var(--tw-bg-opacity))}#app .bg-gray-200{--tw-bg-opacity: 1;background-color:rgba(229,231,235,var(--tw-bg-opacity))}#app .bg-opacity-30{--tw-bg-opacity: .3}#app .px-2{padding-left:.5rem;padding-right:.5rem}#app .py-1{padding-top:.25rem;padding-bottom:.25rem}#app .pr-2{padding-right:.5rem}#app .pr-4{padding-right:1rem}#app .pb-5{padding-bottom:1.25rem}#app .pl-3{padding-left:.75rem}#app .pl-4{padding-left:1rem}#app .text-center{text-align:center}#app .text-right{text-align:right}#app .text-xs{font-size:.75rem;line-height:1rem}#app .text-sm{font-size:.875rem;line-height:1.25rem}#app .text-base{font-size:1rem;line-height:1.5rem}#app .text-2xl{font-size:1.5rem;line-height:2rem}#app .font-normal{font-weight:400}#app .font-medium{font-weight:500}#app .font-bold{font-weight:700}#app .leading-6{line-height:1.5rem}#app .leading-normal{line-height:1.5}#app .text-blue-400{--tw-text-opacity: 1;color:rgba(96,165,250,var(--tw-text-opacity))}*,:before,:after{--tw-shadow: 0 0 #0000}#app .shadow-md{--tw-shadow: 0 4px 6px -1px rgba(0, 0, 0, .1), 0 2px 4px -1px rgba(0, 0, 0, .06);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}#app .shadow-lg{--tw-shadow: 0 10px 15px -3px rgba(0, 0, 0, .1), 0 4px 6px -2px rgba(0, 0, 0, .05);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}*,:before,:after{--tw-ring-inset: var(--tw-empty, );--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgba(59, 130, 246, .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000}#app .drop-shadow-sm{--tw-drop-shadow: drop-shadow(0 1px 1px rgba(0,0,0,.05))}#app .backdrop-filter{--tw-backdrop-blur: var(--tw-empty, );--tw-backdrop-brightness: var(--tw-empty, );--tw-backdrop-contrast: var(--tw-empty, );--tw-backdrop-grayscale: var(--tw-empty, );--tw-backdrop-hue-rotate: var(--tw-empty, );--tw-backdrop-invert: var(--tw-empty, );--tw-backdrop-opacity: var(--tw-empty, );--tw-backdrop-saturate: var(--tw-empty, );--tw-backdrop-sepia: var(--tw-empty, );-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}#app .backdrop-blur-sm{--tw-backdrop-blur: blur(4px)}#app .transition{transition-property:background-color,border-color,color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-backdrop-filter;transition-property:background-color,border-color,color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-property:background-color,border-color,color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter,-webkit-backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}a[data-v-71afa157]:link{color:#b321ff}a[data-v-71afa157]{color:#eecdff}a[data-v-71afa157]:hover{color:red}a[data-v-7065ab79]:link{color:#b321ff}a[data-v-7065ab79]{color:#eecdff}a[data-v-7065ab79]:hover{color:red}
2 |
--------------------------------------------------------------------------------