diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 4be06957..f8029249 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -30,9 +30,11 @@ jobs: echo "${DOCKER_PASSWORD}" | docker login --username "${DOCKER_USERNAME}" --password-stdin - name: Docker buildx image and push on master branch + env: + SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }} if: github.ref == 'refs/heads/master' run: | - docker buildx build --output "type=image,push=true" --platform=linux/amd64,linux/arm/v7,linux/arm64 --tag whyour/qinglong:latest docker/ + docker buildx build --build-arg SSH_PRIVATE_KEY="${SSH_PRIVATE_KEY}" --output "type=image,push=true" --platform=linux/amd64,linux/arm/v7,linux/arm64 --tag whyour/qinglong:latest docker/ - name: Replace tag without `v` if: startsWith(github.ref, 'refs/tags/') @@ -44,7 +46,9 @@ jobs: result-encoding: string - name: Docker buildx image and push on release + env: + SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }} if: startsWith(github.ref, 'refs/tags/') run: | - docker buildx build --output "type=image,push=true" --platform=linux/amd64,linux/arm/v7,linux/arm64 --tag whyour/qinglong:${{steps.version.outputs.result}} docker/ - docker buildx build --output "type=image,push=true" --platform=linux/amd64,linux/arm/v7,linux/arm64 --tag whyour/qinglong:latest docker/ \ No newline at end of file + docker buildx build --build-arg SSH_PRIVATE_KEY="${SSH_PRIVATE_KEY}" --output "type=image,push=true" --platform=linux/amd64,linux/arm/v7,linux/arm64 --tag whyour/qinglong:${{steps.version.outputs.result}} docker/ + docker buildx build --build-arg SSH_PRIVATE_KEY="${SSH_PRIVATE_KEY}" --output "type=image,push=true" --platform=linux/amd64,linux/arm/v7,linux/arm64 --tag whyour/qinglong:latest docker/ \ No newline at end of file diff --git a/.umirc.ts b/.umirc.ts index 2fbf3ffb..94e49782 100644 --- a/.umirc.ts +++ b/.umirc.ts @@ -8,7 +8,7 @@ export default defineConfig({ type: 'none', }, fastRefresh: {}, - favicon: 'https://image.whyour.cn/others/g5.ico', + favicon: 'https://qinglong.whyour.cn/g5.ico', proxy: { '/api': { target: 'http://127.0.0.1:5678/', diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..ce3bec2f --- /dev/null +++ b/LICENSE @@ -0,0 +1,22 @@ +MIT LICENSE + +Copyright (c) 2021-present whyour + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md index 8951f341..65d0c66f 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,51 @@ -## 青龙(WIP) +

+ + + +

+ +

青龙(WIP)

+ +
+ +python和javaScript的定时任务管理面板 + +[![donate][donate-image]][donate-url] [![build status][build-status-image]][build-status-url] [![docker pulls][docker-pulls-image]][docker-pulls-url] [![docker version][docker-version-image]][docker-version-url] [![docker stars][docker-stars-image]][docker-stars-url] [![docker image size][docker-image-size-image]][docker-image-size-url] + +[donate-image]: https://img.shields.io/badge/donate-wechat-green?style=for-the-badge +[donate-url]: https://qinglong.whyour.cn/nice.png +[build-status-image]: https://img.shields.io/docker/cloud/build/whyour/qinglong?style=for-the-badge +[build-status-url]: https://img.shields.io/docker/cloud/build/whyour/qinglong +[docker-pulls-image]: https://img.shields.io/docker/pulls/whyour/qinglong?style=for-the-badge +[docker-pulls-url]: https://hub.docker.com/r/whyour/qinglong +[docker-version-image]: https://img.shields.io/docker/v/whyour/qinglong?style=for-the-badge +[docker-version-url]: https://hub.docker.com/r/whyour/qinglong/tags?page=1&ordering=last_updated +[docker-stars-image]: https://img.shields.io/docker/stars/whyour/qinglong?style=for-the-badge +[docker-stars-url]: https://hub.docker.com/r/whyour/qinglong +[docker-image-size-image]: https://img.shields.io/docker/image-size/whyour/qinglong?style=for-the-badge +[docker-image-size-url]: https://hub.docker.com/r/whyour/qinglong + +
青龙,又名苍龙,在中国传统文化中是四象之一、[天之四灵](https://zh.wikipedia.org/wiki/%E5%A4%A9%E4%B9%8B%E5%9B%9B%E7%81%B5)之一,根据五行学说,它是代表东方的灵兽,为青色的龙,五行属木,代表的季节是春季,八卦主震。苍龙与应龙一样,都是身具羽翼。《张果星宗》称“又有辅翼,方为真龙”。 《后汉书·律历志下》记载:日周于天,一寒一暑,四时备成,万物毕改,摄提迁次,青龙移辰,谓之岁。 -在中国[二十八宿](https://zh.wikipedia.org/wiki/%E4%BA%8C%E5%8D%81%E5%85%AB%E5%AE%BF)中,青龙是东方七宿(角、亢、氐、房、心、尾、箕)的总称。 在早期星宿信仰中,祂是最尊贵的天神[1]。 但被道教信仰吸纳入其神系后,神格大跌,道教将其称为“孟章”,在不同的道经中有“帝君”、“圣将”、“神将”和“捕鬼将”等称呼[2],与白虎监兵神君一起,是道教的护卫天神。 +在中国[二十八宿](https://zh.wikipedia.org/wiki/%E4%BA%8C%E5%8D%81%E5%85%AB%E5%AE%BF)中,青龙是东方七宿(角、亢、氐、房、心、尾、箕)的总称。 在早期星宿信仰中,祂是最尊贵的天神。 但被道教信仰吸纳入其神系后,神格大跌,道教将其称为“孟章”,在不同的道经中有“帝君”、“圣将”、“神将”和“捕鬼将”等称呼,与白虎监兵神君一起,是道教的护卫天神。 ## 多谢 -[https://github.com/nevinee/jd_shell](https://github.com/nevinee/jd_shell) +* [https://github.com/nevinee/jd_shell](https://github.com/nevinee/jd_shell) -[https://github.com/alseambusher/crontab-ui](https://github.com/alseambusher/crontab-ui) +* [https://github.com/alseambusher/crontab-ui](https://github.com/alseambusher/crontab-ui) + +* [Ant Design](https://ant.design) + +* [Ant Design Pro](https://pro.ant.design/) + +* [Umijs3.0](https://umijs.org) + +* [darkreader](https://github.com/darkreader/darkreader) ## 免责声明 @@ -27,8 +62,3 @@ 6. 如果任何单位或个人认为此仓储脚本可能涉嫌侵犯其权利,应及时通知并提供身份证明,所有权证明,我们将在收到认证文件确认后删除此仓储脚本。 7. 所有直接或间接使用、查看此仓储脚本的人均应该仔细阅读此声明。本人保留随时更改或补充此声明的权利。一旦您使用或复制了此仓储脚本,即视为您已接受此免责声明。 - - -### one more thing - -![one-more-thing](https://image.whyour.cn/others/nice.png) diff --git a/back/loaders/initData.ts b/back/loaders/initData.ts index 7bf1545c..7a39bc0c 100644 --- a/back/loaders/initData.ts +++ b/back/loaders/initData.ts @@ -16,23 +16,17 @@ const initData = [ status: CrontabStatus.idle, }, { - name: '自定义仓库', - command: `sleep ${randomSchedule( - 60, - 1, - )} && diy whyour hundun "quanx/jx|quanx/jd" tokens >> $QL_DIR/log/diy_pull.log 2>&1`, - schedule: `${randomSchedule(60, 1)} ${randomSchedule( - 24, - 6, - ).toString()} * * *`, - status: CrontabStatus.idle, + name: 'build面板', + command: 'rebuild >> ${QL_DIR}/log/rebuild.log 2>&1', + schedule: '30 7 */7 * *', + status: CrontabStatus.disabled, }, { name: '自定义仓库', command: `sleep ${randomSchedule( 60, 1, - )} && diy monk-coder dust "i-chenzhe|normal" >> $QL_DIR/log/diy_pull.log 2>&1`, + )} && diy https://ghproxy.com/https://github.com/whyour/hundun.git "quanx/jx|quanx/jd" tokens >> $QL_DIR/log/diy_pull.log 2>&1`, schedule: `${randomSchedule(60, 1)} ${randomSchedule( 24, 6, @@ -45,12 +39,6 @@ const initData = [ schedule: '48 5 * * *', status: CrontabStatus.idle, }, - { - name: 'build面板', - command: 'rebuild >> ${QL_DIR}/log/rebuild.log 2>&1', - schedule: '30 7 */7 * *', - status: CrontabStatus.disabled, - }, { name: '删除日志', command: 'rm_log >/dev/null 2>&1', diff --git a/back/services/cookie.ts b/back/services/cookie.ts index 689e6abc..76a596ba 100644 --- a/back/services/cookie.ts +++ b/back/services/cookie.ts @@ -81,10 +81,11 @@ export default class CookieService { public async refreshCookie(_id: string) { const current = await this.get(_id); - const { status } = await this.getJdInfo(current.value); + const { status, nickname } = await this.getJdInfo(current.value); return { ...current, status, + nickname, }; } @@ -121,14 +122,15 @@ export default class CookieService { } public async create(payload: string[]): Promise { - const cookies = await this.cookies('', { postion: 1 }); + const cookies = await this.cookies(''); let position = initCookiePosition; if (cookies && cookies.length > 0) { - position = cookies[0].position / 2; + position = cookies[cookies.length - 1].position; } const tabs = payload.map((x) => { const cookie = new Cookie({ value: x, position }); position = position / 2; + cookie.position = position; return cookie; }); const docs = await this.insert(tabs); diff --git a/back/services/cron.ts b/back/services/cron.ts index 5ea4dcfc..da084bab 100644 --- a/back/services/cron.ts +++ b/back/services/cron.ts @@ -48,7 +48,7 @@ export default class CronService { const doc = await this.get(_id); const tab = new Crontab({ ...doc, ...other }); tab.saved = false; - const newDoc = await this.update(tab); + const newDoc = await this.updateDb(tab); await this.set_crontab(); return newDoc; } diff --git a/docker/Dockerfile b/docker/Dockerfile index fe073563..6dc6ebc4 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -15,9 +15,7 @@ RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories coreutils \ moreutils \ git \ - wget \ curl \ - nano \ tzdata \ perl \ openssl \ diff --git a/docker/docker-entrypoint.sh b/docker/docker-entrypoint.sh index 89d6d0f5..72bc1d28 100644 --- a/docker/docker-entrypoint.sh +++ b/docker/docker-entrypoint.sh @@ -7,6 +7,9 @@ echo echo -e "======================2. 检测配置文件========================\n" [ ! -d ${QL_DIR}/config ] && mkdir -p ${QL_DIR}/config +[ ! -d ${QL_DIR}/log ] && mkdir -p ${QL_DIR}/log +[ ! -d ${QL_DIR}/db ] && mkdir -p ${QL_DIR}/db +[ ! -d ${QL_DIR}/manual_log ] && mkdir -p ${QL_DIR}/manual_log if [ ! -s ${QL_DIR}/config/crontab.list ] then @@ -52,6 +55,9 @@ pm2 start ${QL_DIR}/build/app.js -n panel echo -e "控制面板启动成功...\n" echo -e "\n容器启动成功...\n" +echo -e "\n请先访问5700端口,登录面板成功之后先手动执行一次git_pull命令...\n" +echo -e "\n如果需要启动挂机程序手动执行docker exec -it qinglong js hangup...\n" +echo -e "\n或者去cron管理搜索hangup手动执行挂机任务...\n" crond -f diff --git a/shell/rebuild.sh b/shell/rebuild.sh index 24c1c233..ecec1823 100644 --- a/shell/rebuild.sh +++ b/shell/rebuild.sh @@ -13,6 +13,7 @@ echo -e "重新build...\n" yarn install --network-timeout 1000000000 || yarn install --registry=https://registry.npm.taobao.org --network-timeout 1000000000 yarn build yarn build-back +yarn cache clean echo -e "重新build完成...\n" echo -e "重启服务...\n" diff --git a/src/app.tsx b/src/app.tsx index 2e3aef04..ca5c3221 100644 --- a/src/app.tsx +++ b/src/app.tsx @@ -3,9 +3,6 @@ import { request } from '@/utils/http'; import config from '@/utils/config'; export function render(oldRender: any) { - if (history.location.pathname === '/login') { - oldRender(); - } request .get(`${config.apiPrefix}user`) .then((data) => { diff --git a/src/assets/logo.png b/src/assets/logo.png index 096c16cb..0a53e4e8 100644 Binary files a/src/assets/logo.png and b/src/assets/logo.png differ diff --git a/src/layouts/defaultProps.tsx b/src/layouts/defaultProps.tsx index 59b44c58..3e50384a 100644 --- a/src/layouts/defaultProps.tsx +++ b/src/layouts/defaultProps.tsx @@ -8,7 +8,6 @@ import { RadiusSettingOutlined, ControlOutlined, } from '@ant-design/icons'; -import logo from '@/assets/logo.png'; export default { route: { @@ -70,5 +69,5 @@ export default { fixSiderbar: true, contentWidth: 'Fixed', splitMenus: false, - logo: logo, + logo: 'https://qinglong.whyour.cn/qinglong.png', } as any; diff --git a/src/layouts/index.less b/src/layouts/index.less index c1fe4a56..e0f8d31d 100644 --- a/src/layouts/index.less +++ b/src/layouts/index.less @@ -23,6 +23,12 @@ body { } } +.cookie-wrapper { + th { + white-space: nowrap; + } +} + @media (max-width: 768px) { .ant-pro-grid-content.wide { .ant-pro-page-container-children-content { diff --git a/src/pages/cookie/index.tsx b/src/pages/cookie/index.tsx index 8ace424c..71ccfaeb 100644 --- a/src/pages/cookie/index.tsx +++ b/src/pages/cookie/index.tsx @@ -113,8 +113,18 @@ const Config = () => { render: (text: string, record: any) => { const match = record.value.match(/pt_pin=([^; ]+)(?=;?)/); const val = (match && match[1]) || '未匹配用户名'; + return {decodeUrl(val)}; + }, + }, + { + title: '昵称', + dataIndex: 'nickname', + key: 'nickname', + align: 'center' as const, + width: '15%', + render: (text: string, record: any, index: number) => { return ( - {decodeURIComponent(val)} + {record.nickname || '-'} ); }, }, @@ -212,6 +222,36 @@ const Config = () => { .finally(() => setLoading(false)); }; + const decodeUrl = (val: string) => { + try { + const newUrl = decodeURIComponent(val); + return newUrl; + } catch (error) { + return val; + } + }; + + useEffect(() => { + if (value && loading) { + asyncUpdateStatus(); + } + }, [value]); + + const asyncUpdateStatus = async () => { + for (let i = 0; i < value.length; i++) { + const cookie = value[i]; + if (cookie.status === Status.已禁用) { + continue; + } + await sleep(1000); + location.pathname === '/cookie' && refreshStatus(cookie, i); + } + }; + + const sleep = (time: number) => { + return new Promise((resolve) => setTimeout(resolve, time)); + }; + const refreshStatus = (record: any, index: number) => { request .get(`${config.apiPrefix}cookies/${record._id}/refresh`) @@ -322,24 +362,28 @@ const Config = () => { }); }; - const handleCancel = (cookie: any) => { + const handleCancel = (cookies: any[]) => { setIsModalVisible(false); - if (cookie) { - handleCookies(cookie); + if (cookies && cookies.length > 0) { + handleCookies(cookies); } }; - const handleCookies = (cookie: any) => { - const index = value.findIndex((x) => x._id === cookie._id); - const result = [...value]; - if (index === -1) { - result.push(...cookie); - } else { - result.splice(index, 1, { - ...cookie, - }); + const handleCookies = (cookies: any[]) => { + for (let i = 0; i < cookies.length; i++) { + const cookie = cookies[i]; + const index = value.findIndex((x) => x._id === cookie._id); + const result = [...value]; + if (index === -1) { + result.push(cookie); + } else { + result.splice(index, 1, { + ...cookie, + }); + } + setValue(result); + refreshStatus(cookie, index); } - setValue(result); }; const components = { @@ -388,7 +432,7 @@ const Config = () => { return ( void; }) => { const [form] = Form.useForm(); + const [loading, setLoading] = useState(false); const handleOk = async (values: any) => { const cookies = values.value @@ -29,6 +30,7 @@ const CookieModal = ({ if (flag) { return; } + setLoading(true); const method = cookie ? 'put' : 'post'; const payload = cookie ? { value: cookies[0], _id: cookie._id } : cookies; const { code, data } = await request[method](`${config.apiPrefix}cookies`, { @@ -43,6 +45,7 @@ const CookieModal = ({ message: data, }); } + setLoading(false); handleCancel(data); }; @@ -66,6 +69,7 @@ const CookieModal = ({ }); }} onCancel={() => handleCancel()} + confirmLoading={loading} destroyOnClose >
void; }) => { const [form] = Form.useForm(); + const [loading, setLoading] = useState(false); const handleOk = async (values: any) => { + setLoading(true); const method = cron ? 'put' : 'post'; const payload = { ...values }; if (cron) { @@ -33,6 +35,7 @@ const CronModal = ({ message: data, }); } + setLoading(false); handleCancel(data); }; @@ -60,6 +63,7 @@ const CronModal = ({ }); }} onCancel={() => handleCancel()} + confirmLoading={loading} destroyOnClose > diff --git a/src/pages/login/index.less b/src/pages/login/index.less index 23075564..3892be68 100644 --- a/src/pages/login/index.less +++ b/src/pages/login/index.less @@ -51,7 +51,7 @@ } .logo { - width: 40px; + width: 30px; margin-right: 8px; } diff --git a/src/pages/login/index.tsx b/src/pages/login/index.tsx index c8d0ee4d..fe4cf969 100644 --- a/src/pages/login/index.tsx +++ b/src/pages/login/index.tsx @@ -4,7 +4,6 @@ import config from '@/utils/config'; import { history, Link } from 'umi'; import styles from './index.less'; import { request } from '@/utils/http'; -import logo from '@/assets/logo.png'; const FormItem = Form.Item; @@ -48,7 +47,11 @@ const Login = () => {
- logo + logo {config.siteName}
diff --git a/src/utils/config.ts b/src/utils/config.ts index 7763b213..4cbe07a1 100644 --- a/src/utils/config.ts +++ b/src/utils/config.ts @@ -1,5 +1,5 @@ export default { - siteName: '脚本控制面板', + siteName: '青龙控制面板', apiPrefix: '/api/', authKey: 'token',