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
-
-
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
>