全新青龙2.0 (#65)

* 重构shell (#17)

* 更新正则

* 更新update命令

* 移除测试代码

* 重构删除日志命令

* 更新entrypoint

* 更新dockerfile

* 完善shell调用

* 修复share shell引用

* 修复entrypoint

* 修复share shell

* 修复share.sh

* 修改依赖重装逻辑

* 更新docker entrypoint

* curl 使用静默模式

* 更新ql raw

* 修复添加单个任务

* 修复shell语法

* 添加定时任务进程

* 更新默认定时任务

* 更新定时任务重启schedule

* 更新青龙重启逻辑

* 修复定时任务列表创建

* 修复schedule进程

* 修复entrypoint

* 更新task命令

* pm2 restart替换成reload

* 修复task命令参数引入

* 完善ql repo命令

* 修复update.sh

* 更新ql repo命令

* ql repo添加登录验证,修复package.json示例

* 修复定时任务命令补全

* 修改默认cron端口

* 修复cron日志弹框异常

* 修改cron新建label

* 修复ql repo命令

* 修复cron格式验证

* 修改日志目录格式

* 修改青龙remote url

* 修复添加定时cron匹配

* 添加定时任务超时时间设置

* 暂时移除timeout命令

* 恢复定时任务timeout

* 修复cookie.sh引用

* 修复shell变量自加

* 修复ck更新状态同步

* 增加tg bot测试,修改增删任务通知

* 修复shell函数返回值

* 修改添加任务日志打印

* 修改entrypoint日志

* 修复api日志打印

* 修改api日志打印

* 定时任务支持批量启用禁用删除运行

* 修改cron管理操作按钮响应样式

* 更新bot启动脚本

* 更新bot启动脚本

* 增加timeout默认值,修改session管理逻辑

* 更新config示例和通知日志

* 更新bot.sh

* 更新启动bot命令

* 更新启动bot命令

* 修复task运行参数合并

* 增加停止定时任务功能

* 修复停止定时任务api

* 更新停止定时任务日志

* 更新停止任务日志

* 修复删除cron api

* 更新删除cron通知文本

* 更新命令提示

* 更新bot启动脚本
This commit is contained in:
whyour
2021-05-10 20:47:23 +08:00
committed by GitHub
parent 489846d2e6
commit 5b3687f7b6
44 changed files with 2868 additions and 1923 deletions
+22 -24
View File
@@ -105,17 +105,17 @@ const Config = () => {
return <span style={{ cursor: 'text' }}>{index + 1} </span>;
},
},
{
title: '用户名',
dataIndex: 'pin',
key: 'pin',
align: 'center' as const,
render: (text: string, record: any) => {
const match = record.value.match(/pt_pin=([^; ]+)(?=;?)/);
const val = (match && match[1]) || '未匹配用户名';
return <span style={{ cursor: 'text' }}>{decodeUrl(val)}</span>;
},
},
// {
// title: '用户名',
// dataIndex: 'pin',
// key: 'pin',
// align: 'center' as const,
// render: (text: string, record: any) => {
// const match = record.value.match(/pt_pin=([^; ]+)(?=;?)/);
// const val = (match && match[1]) || '未匹配用户名';
// return <span style={{ cursor: 'text' }}>{decodeUrl(val)}</span>;
// },
// },
{
title: '昵称',
dataIndex: 'nickname',
@@ -231,11 +231,11 @@ const Config = () => {
}
};
useEffect(() => {
if (value && loading) {
asyncUpdateStatus();
}
}, [value]);
// useEffect(() => {
// if (value && loading) {
// asyncUpdateStatus();
// }
// }, [value]);
const asyncUpdateStatus = async () => {
for (let i = 0; i < value.length; i++) {
@@ -362,7 +362,7 @@ const Config = () => {
});
};
const handleCancel = (cookies: any[]) => {
const handleCancel = (cookies?: any[]) => {
setIsModalVisible(false);
if (cookies && cookies.length > 0) {
handleCookies(cookies);
@@ -370,10 +370,10 @@ const Config = () => {
};
const handleCookies = (cookies: any[]) => {
const result = [...value];
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 {
@@ -381,9 +381,8 @@ const Config = () => {
...cookie,
});
}
setValue(result);
refreshStatus(cookie, index);
}
setValue(result);
};
const components = {
@@ -432,9 +431,8 @@ const Config = () => {
return (
<PageContainer
className="cookie-wrapper"
title="Cookie管理"
loading={loading}
className="session-wrapper"
title="Session管理"
extra={[
<Button key="2" type="primary" onClick={() => addCookie()}>
Cookie
@@ -463,9 +461,9 @@ const Config = () => {
dataSource={value}
rowKey="value"
size="middle"
bordered
scroll={{ x: 768 }}
components={components}
loading={loading}
onRow={(record, index) => {
return {
index,
+3 -3
View File
@@ -10,7 +10,7 @@ const CookieModal = ({
}: {
cookie?: any;
visible: boolean;
handleCancel: (needUpdate?: boolean) => void;
handleCancel: (cks?: any[]) => void;
}) => {
const [form] = Form.useForm();
const [loading, setLoading] = useState(false);
@@ -46,7 +46,7 @@ const CookieModal = ({
});
}
setLoading(false);
handleCancel(data);
handleCancel(cookie ? [data] : data);
};
useEffect(() => {
@@ -92,7 +92,7 @@ const CookieModal = ({
<Input.TextArea
rows={4}
autoSize={true}
placeholder="请输入cookie,多个cookie换行输入"
placeholder="请输入cookie可直接换行输入多个cookie"
/>
</Form.Item>
</Form>
+205 -39
View File
@@ -23,6 +23,7 @@ import {
EditOutlined,
StopOutlined,
DeleteOutlined,
PauseCircleOutlined,
} from '@ant-design/icons';
import config from '@/utils/config';
import { PageContainer } from '@ant-design/pro-layout';
@@ -39,6 +40,20 @@ enum CrontabStatus {
'disabled',
}
enum OperationName {
'启用',
'禁用',
'运行',
'停止',
}
enum OperationPath {
'enable',
'disable',
'run',
'stop',
}
const Crontab = () => {
const columns = [
{
@@ -108,15 +123,28 @@ const Crontab = () => {
align: 'center' as const,
render: (text: string, record: any, index: number) => (
<Space size="middle">
<Tooltip title="运行">
<a
onClick={() => {
runCron(record, index);
}}
>
<PlayCircleOutlined />
</a>
</Tooltip>
{record.status !== CrontabStatus.running && (
<Tooltip title="运行">
<a
onClick={() => {
runCron(record, index);
}}
>
<PlayCircleOutlined />
</a>
</Tooltip>
)}
{record.status === CrontabStatus.running && (
<Tooltip title="停止">
<a
onClick={() => {
stopCron(record, index);
}}
>
<PauseCircleOutlined />
</a>
</Tooltip>
)}
<Tooltip title="日志">
<a
onClick={() => {
@@ -142,6 +170,7 @@ const Crontab = () => {
const [searchText, setSearchText] = useState('');
const [isLogModalVisible, setIsLogModalVisible] = useState(false);
const [logCron, setLogCron] = useState<any>();
const [selectedRowIds, setSelectedRowIds] = useState<string[]>([]);
const getCrons = () => {
setLoading(true);
@@ -177,7 +206,7 @@ const Crontab = () => {
),
onOk() {
request
.delete(`${config.apiPrefix}crons/${record._id}`)
.delete(`${config.apiPrefix}crons`, { data: [record._id] })
.then((data: any) => {
if (data.code === 200) {
notification.success({
@@ -213,7 +242,7 @@ const Crontab = () => {
),
onOk() {
request
.get(`${config.apiPrefix}crons/${record._id}/run`)
.put(`${config.apiPrefix}crons/run`, { data: [record._id] })
.then((data: any) => {
if (data.code === 200) {
const result = [...value];
@@ -235,6 +264,42 @@ const Crontab = () => {
});
};
const stopCron = (record: any, index: number) => {
Modal.confirm({
title: '确认停止',
content: (
<>
{' '}
<Text style={{ wordBreak: 'break-all' }} type="warning">
{record.name}
</Text>{' '}
</>
),
onOk() {
request
.put(`${config.apiPrefix}crons/stop`, { data: [record._id] })
.then((data: any) => {
if (data.code === 200) {
const result = [...value];
result.splice(index, 1, {
...record,
status: CrontabStatus.idle,
});
setValue(result);
} else {
notification.error({
message: data,
});
}
});
},
onCancel() {
console.log('Cancel');
},
});
};
const enabledOrDisabledCron = (record: any, index: number) => {
Modal.confirm({
title: `确认${
@@ -252,21 +317,16 @@ const Crontab = () => {
),
onOk() {
request
.get(
`${config.apiPrefix}crons/${record._id}/${
.put(
`${config.apiPrefix}crons/${
record.status === CrontabStatus.disabled ? 'enable' : 'disable'
}`,
{
data: { _id: record._id },
data: [record._id],
},
)
.then((data: any) => {
if (data.code === 200) {
notification.success({
message: `${
record.status === CrontabStatus.disabled ? '启用' : '禁用'
}成功`,
});
const newStatus =
record.status === CrontabStatus.disabled
? CrontabStatus.idle
@@ -296,30 +356,28 @@ const Crontab = () => {
}> = ({ record, index }) => (
<Dropdown
arrow
trigger={['click', 'hover']}
trigger={['click']}
overlay={
<Menu onClick={({ key }) => action(key, record, index)}>
<Menu.Item key="edit" icon={<EditOutlined />}>
</Menu.Item>
<Menu.Item
key="enableordisable"
icon={
record.status === CrontabStatus.disabled ? (
<CheckCircleOutlined />
) : (
<StopOutlined />
)
}
>
{record.status === CrontabStatus.disabled ? '启用' : '禁用'}
</Menu.Item>
{record.isSystem !== 1 && (
<>
<Menu.Item
key="enableordisable"
icon={
record.status === CrontabStatus.disabled ? (
<CheckCircleOutlined />
) : (
<StopOutlined />
)
}
>
{record.status === CrontabStatus.disabled ? '启用' : '禁用'}
</Menu.Item>
<Menu.Item key="delete" icon={<DeleteOutlined />}>
</Menu.Item>
</>
<Menu.Item key="delete" icon={<DeleteOutlined />}>
</Menu.Item>
)}
</Menu>
}
@@ -385,8 +443,78 @@ const Crontab = () => {
.finally(() => setLoading(false));
};
const onSelectChange = (selectedIds: any[]) => {
setSelectedRowIds(selectedIds);
};
const rowSelection = {
selectedRowIds,
onChange: onSelectChange,
selections: [
Table.SELECTION_ALL,
Table.SELECTION_INVERT,
Table.SELECTION_NONE,
],
};
const delCrons = () => {
Modal.confirm({
title: '确认删除',
content: <></>,
onOk() {
request
.delete(`${config.apiPrefix}crons`, { data: selectedRowIds })
.then((data: any) => {
if (data.code === 200) {
notification.success({
message: '批量删除成功',
});
setSelectedRowIds([]);
getCrons();
} else {
notification.error({
message: data,
});
}
});
},
onCancel() {
console.log('Cancel');
},
});
};
const operateCrons = (operationStatus: number) => {
Modal.confirm({
title: `确认${OperationName[operationStatus]}`,
content: <>{OperationName[operationStatus]}</>,
onOk() {
request
.put(`${config.apiPrefix}crons/${OperationPath[operationStatus]}`, {
data: selectedRowIds,
})
.then((data: any) => {
if (data.code === 200) {
notification.success({
message: `批量${OperationName[operationStatus]}成功`,
});
getCrons();
} else {
notification.error({
message: data,
});
}
});
},
onCancel() {
console.log('Cancel');
},
});
};
useEffect(() => {
if (logCron) {
localStorage.setItem('logCron', logCron._id);
setIsLogModalVisible(true);
}
}, [logCron]);
@@ -411,7 +539,6 @@ const Crontab = () => {
<PageContainer
className="code-mirror-wrapper"
title="定时任务"
loading={loading}
extra={[
<Search
placeholder="请输入名称或者关键词"
@@ -436,7 +563,45 @@ const Crontab = () => {
marginLeft,
},
}}
style={{
height: '100vh',
}}
>
{selectedRowIds.length > 0 && (
<div style={{ marginBottom: 16 }}>
<Button type="primary" style={{ marginBottom: 5 }} onClick={delCrons}>
</Button>
<Button
type="primary"
onClick={() => operateCrons(0)}
style={{ marginLeft: 8, marginBottom: 5 }}
>
</Button>
<Button
type="primary"
onClick={() => operateCrons(1)}
style={{ marginLeft: 8, marginRight: 8 }}
>
</Button>
<Button
type="primary"
style={{ marginRight: 8 }}
onClick={() => operateCrons(2)}
>
</Button>
<Button type="primary" onClick={() => operateCrons(3)}>
</Button>
<span style={{ marginLeft: 8 }}>
<a>{selectedRowIds?.length}</a>
</span>
</div>
)}
<Table
columns={columns}
pagination={{
@@ -447,8 +612,9 @@ const Crontab = () => {
dataSource={value}
rowKey="_id"
size="middle"
bordered
scroll={{ x: 768 }}
loading={loading}
rowSelection={rowSelection}
/>
<CronLogModal
visible={isLogModalVisible}
+8 -6
View File
@@ -28,12 +28,14 @@ const CronLogModal = ({
request
.get(`${config.apiPrefix}crons/${cron._id}/log`)
.then((data: any) => {
const log = data.data as string;
setValue(log || '暂无日志');
if (log && !log.includes('执行结束')) {
setTimeout(() => {
getCronLog();
}, 2000);
if (localStorage.getItem('logCron') === cron._id) {
const log = data.data as string;
setValue(log || '暂无日志');
if (log && !log.includes('执行结束')) {
setTimeout(() => {
getCronLog();
}, 2000);
}
}
})
.finally(() => {
+6 -6
View File
@@ -68,19 +68,19 @@ const CronModal = ({
>
<Form form={form} layout="vertical" name="form_in_modal" preserve={false}>
<Form.Item name="name" label="名称">
<Input />
<Input placeholder="请输入任务名称" />
</Form.Item>
<Form.Item name="command" label="任务" rules={[{ required: true }]}>
<Input />
<Form.Item name="command" label="命令" rules={[{ required: true }]}>
<Input placeholder="请输入要执行的命令" />
</Form.Item>
<Form.Item
name="schedule"
label="时间"
label="定时规则"
rules={[
{ required: true },
{
validator: (rule, value) => {
if (cronParse.parseString(value).expressions.length > 0) {
if (cronParse.parseExpression(value).hasNext()) {
return Promise.resolve();
} else {
return Promise.reject('Cron表达式格式有误');
@@ -89,7 +89,7 @@ const CronModal = ({
},
]}
>
<Input />
<Input placeholder="秒(可选) 分 时 天 月 周" />
</Form.Item>
</Form>
</Modal>
+1 -1
View File
@@ -87,7 +87,7 @@ const Crontab = () => {
newValue={sample}
splitView={true}
leftTitle="config.sh"
rightTitle="config.sh.sample"
rightTitle="config.sample.sh"
disableWordDiff={true}
/>
{/* <CodeDiff