mirror of
https://github.com/whyour/qinglong.git
synced 2025-05-23 14:56:07 +08:00
更新新建文件订阅
This commit is contained in:
parent
5523d537dc
commit
f6a122e5ea
|
@ -29,7 +29,7 @@ export default (app: Router) => {
|
||||||
body: Joi.object({
|
body: Joi.object({
|
||||||
type: Joi.string().required(),
|
type: Joi.string().required(),
|
||||||
schedule: Joi.string().optional(),
|
schedule: Joi.string().optional(),
|
||||||
intervalSchedule: Joi.object().optional(),
|
interval_schedule: Joi.object().optional(),
|
||||||
name: Joi.string().optional(),
|
name: Joi.string().optional(),
|
||||||
url: Joi.string().required(),
|
url: Joi.string().required(),
|
||||||
whitelist: Joi.string().optional(),
|
whitelist: Joi.string().optional(),
|
||||||
|
@ -158,7 +158,7 @@ export default (app: Router) => {
|
||||||
body: Joi.object({
|
body: Joi.object({
|
||||||
type: Joi.string().required(),
|
type: Joi.string().required(),
|
||||||
schedule: Joi.string().optional(),
|
schedule: Joi.string().optional(),
|
||||||
intervalSchedule: Joi.object().optional(),
|
interval_schedule: Joi.object().optional(),
|
||||||
name: Joi.string().optional(),
|
name: Joi.string().optional(),
|
||||||
url: Joi.string().required(),
|
url: Joi.string().required(),
|
||||||
whitelist: Joi.string().optional(),
|
whitelist: Joi.string().optional(),
|
||||||
|
|
|
@ -2,13 +2,14 @@ import { sequelize } from '.';
|
||||||
import { DataTypes, Model, ModelDefined } from 'sequelize';
|
import { DataTypes, Model, ModelDefined } from 'sequelize';
|
||||||
import { SimpleIntervalSchedule } from 'toad-scheduler';
|
import { SimpleIntervalSchedule } from 'toad-scheduler';
|
||||||
|
|
||||||
|
type SimpleIntervalScheduleUnit = keyof SimpleIntervalSchedule;
|
||||||
export class Subscription {
|
export class Subscription {
|
||||||
id?: number;
|
id?: number;
|
||||||
name?: string;
|
name?: string;
|
||||||
type?: 'public-repo' | 'private-repo' | 'file';
|
type?: 'public-repo' | 'private-repo' | 'file';
|
||||||
schedule_type?: 'crontab' | 'interval';
|
schedule_type?: 'crontab' | 'interval';
|
||||||
schedule?: string;
|
schedule?: string;
|
||||||
intervalSchedule?: SimpleIntervalSchedule;
|
interval_schedule?: { type: SimpleIntervalScheduleUnit; value: number };
|
||||||
url?: string;
|
url?: string;
|
||||||
whitelist?: string;
|
whitelist?: string;
|
||||||
blacklist?: string;
|
blacklist?: string;
|
||||||
|
@ -20,7 +21,7 @@ export class Subscription {
|
||||||
| { private_key: string }
|
| { private_key: string }
|
||||||
| { username: string; password: string };
|
| { username: string; password: string };
|
||||||
pid?: number;
|
pid?: number;
|
||||||
isDisabled?: 1 | 0;
|
is_disabled?: 1 | 0;
|
||||||
log_path?: string;
|
log_path?: string;
|
||||||
alias: string;
|
alias: string;
|
||||||
command?: string;
|
command?: string;
|
||||||
|
@ -30,6 +31,10 @@ export class Subscription {
|
||||||
this.name = options.name;
|
this.name = options.name;
|
||||||
this.type = options.type;
|
this.type = options.type;
|
||||||
this.schedule = options.schedule;
|
this.schedule = options.schedule;
|
||||||
|
this.status =
|
||||||
|
options.status && SubscriptionStatus[options.status]
|
||||||
|
? options.status
|
||||||
|
: SubscriptionStatus.idle;
|
||||||
this.url = options.url;
|
this.url = options.url;
|
||||||
this.whitelist = options.whitelist;
|
this.whitelist = options.whitelist;
|
||||||
this.blacklist = options.blacklist;
|
this.blacklist = options.blacklist;
|
||||||
|
@ -39,11 +44,11 @@ export class Subscription {
|
||||||
this.pull_type = options.pull_type;
|
this.pull_type = options.pull_type;
|
||||||
this.pull_option = options.pull_option;
|
this.pull_option = options.pull_option;
|
||||||
this.pid = options.pid;
|
this.pid = options.pid;
|
||||||
this.isDisabled = options.isDisabled;
|
this.is_disabled = options.is_disabled;
|
||||||
this.log_path = options.log_path;
|
this.log_path = options.log_path;
|
||||||
this.schedule_type = options.schedule_type;
|
this.schedule_type = options.schedule_type;
|
||||||
this.alias = options.alias;
|
this.alias = options.alias;
|
||||||
this.intervalSchedule = options.intervalSchedule;
|
this.interval_schedule = options.interval_schedule;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,7 +77,7 @@ export const SubscriptionModel = sequelize.define<SubscriptionInstance>(
|
||||||
unique: 'compositeIndex',
|
unique: 'compositeIndex',
|
||||||
type: DataTypes.STRING,
|
type: DataTypes.STRING,
|
||||||
},
|
},
|
||||||
intervalSchedule: {
|
interval_schedule: {
|
||||||
unique: 'compositeIndex',
|
unique: 'compositeIndex',
|
||||||
type: DataTypes.JSON,
|
type: DataTypes.JSON,
|
||||||
},
|
},
|
||||||
|
@ -85,7 +90,7 @@ export const SubscriptionModel = sequelize.define<SubscriptionInstance>(
|
||||||
pull_type: DataTypes.STRING,
|
pull_type: DataTypes.STRING,
|
||||||
pull_option: DataTypes.JSON,
|
pull_option: DataTypes.JSON,
|
||||||
pid: DataTypes.NUMBER,
|
pid: DataTypes.NUMBER,
|
||||||
isDisabled: DataTypes.NUMBER,
|
is_disabled: DataTypes.NUMBER,
|
||||||
log_path: DataTypes.STRING,
|
log_path: DataTypes.STRING,
|
||||||
schedule_type: DataTypes.STRING,
|
schedule_type: DataTypes.STRING,
|
||||||
alias: { type: DataTypes.STRING, unique: 'alias' },
|
alias: { type: DataTypes.STRING, unique: 'alias' },
|
||||||
|
|
|
@ -100,10 +100,11 @@ export default class SubscriptionService {
|
||||||
needCreate && this.scheduleService.createCronTask(doc as any);
|
needCreate && this.scheduleService.createCronTask(doc as any);
|
||||||
} else {
|
} else {
|
||||||
this.scheduleService.cancelIntervalTask(doc as any);
|
this.scheduleService.cancelIntervalTask(doc as any);
|
||||||
|
const { type, value } = doc.interval_schedule as any;
|
||||||
needCreate &&
|
needCreate &&
|
||||||
this.scheduleService.createIntervalTask(
|
this.scheduleService.createIntervalTask(
|
||||||
doc as any,
|
doc as any,
|
||||||
doc.intervalSchedule as SimpleIntervalSchedule,
|
{ [type]: value } as SimpleIntervalSchedule,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -73,7 +73,7 @@ enum OperationPath {
|
||||||
const Crontab = ({ headerStyle, isPhone, theme }: any) => {
|
const Crontab = ({ headerStyle, isPhone, theme }: any) => {
|
||||||
const columns: any = [
|
const columns: any = [
|
||||||
{
|
{
|
||||||
title: '任务名',
|
title: '名称',
|
||||||
dataIndex: 'name',
|
dataIndex: 'name',
|
||||||
key: 'name',
|
key: 'name',
|
||||||
width: 150,
|
width: 150,
|
||||||
|
@ -127,7 +127,7 @@ const Crontab = ({ headerStyle, isPhone, theme }: any) => {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '任务',
|
title: '命令',
|
||||||
dataIndex: 'command',
|
dataIndex: 'command',
|
||||||
key: 'command',
|
key: 'command',
|
||||||
width: 250,
|
width: 250,
|
||||||
|
@ -152,7 +152,7 @@ const Crontab = ({ headerStyle, isPhone, theme }: any) => {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '任务定时',
|
title: '定时规则',
|
||||||
dataIndex: 'schedule',
|
dataIndex: 'schedule',
|
||||||
key: 'schedule',
|
key: 'schedule',
|
||||||
width: 110,
|
width: 110,
|
||||||
|
|
|
@ -10,6 +10,7 @@ import {
|
||||||
Menu,
|
Menu,
|
||||||
Typography,
|
Typography,
|
||||||
Input,
|
Input,
|
||||||
|
Tooltip,
|
||||||
} from 'antd';
|
} from 'antd';
|
||||||
import {
|
import {
|
||||||
ClockCircleOutlined,
|
ClockCircleOutlined,
|
||||||
|
@ -20,6 +21,9 @@ import {
|
||||||
EditOutlined,
|
EditOutlined,
|
||||||
StopOutlined,
|
StopOutlined,
|
||||||
DeleteOutlined,
|
DeleteOutlined,
|
||||||
|
FileTextOutlined,
|
||||||
|
PauseCircleOutlined,
|
||||||
|
PlayCircleOutlined,
|
||||||
} from '@ant-design/icons';
|
} from '@ant-design/icons';
|
||||||
import config from '@/utils/config';
|
import config from '@/utils/config';
|
||||||
import { PageContainer } from '@ant-design/pro-layout';
|
import { PageContainer } from '@ant-design/pro-layout';
|
||||||
|
@ -28,6 +32,7 @@ import SubscriptionModal from './modal';
|
||||||
import { getTableScroll } from '@/utils/index';
|
import { getTableScroll } from '@/utils/index';
|
||||||
import { history } from 'umi';
|
import { history } from 'umi';
|
||||||
import './index.less';
|
import './index.less';
|
||||||
|
import SubscriptionLogModal from './logModal';
|
||||||
|
|
||||||
const { Text, Paragraph } = Typography;
|
const { Text, Paragraph } = Typography;
|
||||||
const { Search } = Input;
|
const { Search } = Input;
|
||||||
|
@ -39,10 +44,17 @@ export enum SubscriptionStatus {
|
||||||
'queued',
|
'queued',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum IntervalSchedule {
|
||||||
|
'days' = '天',
|
||||||
|
'hours' = '时',
|
||||||
|
'minutes' = '分',
|
||||||
|
'seconds' = '秒',
|
||||||
|
}
|
||||||
|
|
||||||
const Subscription = ({ headerStyle, isPhone, theme }: any) => {
|
const Subscription = ({ headerStyle, isPhone, theme }: any) => {
|
||||||
const columns: any = [
|
const columns: any = [
|
||||||
{
|
{
|
||||||
title: '订阅名',
|
title: '名称',
|
||||||
dataIndex: 'name',
|
dataIndex: 'name',
|
||||||
key: 'name',
|
key: 'name',
|
||||||
width: 150,
|
width: 150,
|
||||||
|
@ -53,39 +65,35 @@ const Subscription = ({ headerStyle, isPhone, theme }: any) => {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '订阅',
|
title: '链接',
|
||||||
dataIndex: 'command',
|
dataIndex: 'url',
|
||||||
key: 'command',
|
key: 'url',
|
||||||
width: 250,
|
|
||||||
align: 'center' as const,
|
align: 'center' as const,
|
||||||
render: (text: string, record: any) => {
|
|
||||||
return (
|
|
||||||
<Paragraph
|
|
||||||
style={{
|
|
||||||
wordBreak: 'break-all',
|
|
||||||
marginBottom: 0,
|
|
||||||
textAlign: 'left',
|
|
||||||
}}
|
|
||||||
ellipsis={{ tooltip: text, rows: 2 }}
|
|
||||||
>
|
|
||||||
{text}
|
|
||||||
</Paragraph>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
sorter: {
|
sorter: {
|
||||||
compare: (a: any, b: any) => a.command.localeCompare(b.command),
|
compare: (a: any, b: any) => a.name.localeCompare(b.name),
|
||||||
multiple: 3,
|
multiple: 2,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '订阅定时',
|
title: '分支',
|
||||||
dataIndex: 'schedule',
|
dataIndex: 'branch',
|
||||||
key: 'schedule',
|
key: 'branch',
|
||||||
width: 110,
|
width: 130,
|
||||||
align: 'center' as const,
|
align: 'center' as const,
|
||||||
sorter: {
|
},
|
||||||
compare: (a: any, b: any) => a.schedule.localeCompare(b.schedule),
|
{
|
||||||
multiple: 1,
|
title: '定时规则',
|
||||||
|
width: 180,
|
||||||
|
align: 'center' as const,
|
||||||
|
render: (text: string, record: any) => {
|
||||||
|
const { type, value } = record.interval_schedule;
|
||||||
|
return (
|
||||||
|
<span>
|
||||||
|
{record.schedule_type === 'interval'
|
||||||
|
? `每${value}${(IntervalSchedule as any)[type]}`
|
||||||
|
: record.schedule}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -93,7 +101,7 @@ const Subscription = ({ headerStyle, isPhone, theme }: any) => {
|
||||||
key: 'status',
|
key: 'status',
|
||||||
dataIndex: 'status',
|
dataIndex: 'status',
|
||||||
align: 'center' as const,
|
align: 'center' as const,
|
||||||
width: 85,
|
width: 110,
|
||||||
filters: [
|
filters: [
|
||||||
{
|
{
|
||||||
text: '运行中',
|
text: '运行中',
|
||||||
|
@ -148,10 +156,45 @@ const Subscription = ({ headerStyle, isPhone, theme }: any) => {
|
||||||
title: '操作',
|
title: '操作',
|
||||||
key: 'action',
|
key: 'action',
|
||||||
align: 'center' as const,
|
align: 'center' as const,
|
||||||
width: 100,
|
width: 130,
|
||||||
render: (text: string, record: any, index: number) => {
|
render: (text: string, record: any, index: number) => {
|
||||||
|
const isPc = !isPhone;
|
||||||
return (
|
return (
|
||||||
<Space size="middle">
|
<Space size="middle">
|
||||||
|
{record.status === SubscriptionStatus.idle && (
|
||||||
|
<Tooltip title={isPc ? '运行' : ''}>
|
||||||
|
<a
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
runSubscription(record, index);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<PlayCircleOutlined />
|
||||||
|
</a>
|
||||||
|
</Tooltip>
|
||||||
|
)}
|
||||||
|
{record.status !== SubscriptionStatus.idle && (
|
||||||
|
<Tooltip title={isPc ? '停止' : ''}>
|
||||||
|
<a
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
stopSubsciption(record, index);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<PauseCircleOutlined />
|
||||||
|
</a>
|
||||||
|
</Tooltip>
|
||||||
|
)}
|
||||||
|
<Tooltip title={isPc ? '日志' : ''}>
|
||||||
|
<a
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
setLogSubscription({ ...record, timestamp: Date.now() });
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<FileTextOutlined />
|
||||||
|
</a>
|
||||||
|
</Tooltip>
|
||||||
<MoreBtn key="more" record={record} index={index} />
|
<MoreBtn key="more" record={record} index={index} />
|
||||||
</Space>
|
</Space>
|
||||||
);
|
);
|
||||||
|
@ -168,23 +211,78 @@ const Subscription = ({ headerStyle, isPhone, theme }: any) => {
|
||||||
const [pageSize, setPageSize] = useState(20);
|
const [pageSize, setPageSize] = useState(20);
|
||||||
const [tableScrollHeight, setTableScrollHeight] = useState<number>();
|
const [tableScrollHeight, setTableScrollHeight] = useState<number>();
|
||||||
const [searchValue, setSearchValue] = useState('');
|
const [searchValue, setSearchValue] = useState('');
|
||||||
|
const [isLogModalVisible, setIsLogModalVisible] = useState(false);
|
||||||
|
const [logSubscription, setLogSubscription] = useState<any>();
|
||||||
|
|
||||||
const goToScriptManager = (record: any) => {
|
const runSubscription = (record: any, index: number) => {
|
||||||
const cmd = record.command.split(' ') as string[];
|
Modal.confirm({
|
||||||
if (cmd[0] === 'task') {
|
title: '确认运行',
|
||||||
if (cmd[1].startsWith('/ql/data/scripts')) {
|
content: (
|
||||||
cmd[1] = cmd[1].replace('/ql/data/scripts/', '');
|
<>
|
||||||
|
确认运行定时任务{' '}
|
||||||
|
<Text style={{ wordBreak: 'break-all' }} type="warning">
|
||||||
|
{record.name}
|
||||||
|
</Text>{' '}
|
||||||
|
吗
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
onOk() {
|
||||||
|
request
|
||||||
|
.put(`${config.apiPrefix}subscriptions/run`, { data: [record.id] })
|
||||||
|
.then((data: any) => {
|
||||||
|
if (data.code === 200) {
|
||||||
|
const result = [...value];
|
||||||
|
const i = result.findIndex((x) => x.id === record.id);
|
||||||
|
result.splice(i, 1, {
|
||||||
|
...record,
|
||||||
|
status: SubscriptionStatus.running,
|
||||||
|
});
|
||||||
|
setValue(result);
|
||||||
|
} else {
|
||||||
|
message.error(data);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onCancel() {
|
||||||
|
console.log('Cancel');
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
let [p, s] = cmd[1].split('/');
|
const stopSubsciption = (record: any, index: number) => {
|
||||||
if (!s) {
|
Modal.confirm({
|
||||||
s = p;
|
title: '确认停止',
|
||||||
p = '';
|
content: (
|
||||||
}
|
<>
|
||||||
history.push(`/script?p=${p}&s=${s}`);
|
确认停止定时任务{' '}
|
||||||
} else if (cmd[1] === 'repo') {
|
<Text style={{ wordBreak: 'break-all' }} type="warning">
|
||||||
location.href = cmd[2];
|
{record.name}
|
||||||
|
</Text>{' '}
|
||||||
|
吗
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
onOk() {
|
||||||
|
request
|
||||||
|
.put(`${config.apiPrefix}subscriptions/stop`, { data: [record.id] })
|
||||||
|
.then((data: any) => {
|
||||||
|
if (data.code === 200) {
|
||||||
|
const result = [...value];
|
||||||
|
const i = result.findIndex((x) => x.id === record.id);
|
||||||
|
result.splice(i, 1, {
|
||||||
|
...record,
|
||||||
|
pid: null,
|
||||||
|
status: SubscriptionStatus.idle,
|
||||||
|
});
|
||||||
|
setValue(result);
|
||||||
|
} else {
|
||||||
|
message.error(data);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onCancel() {
|
||||||
|
console.log('Cancel');
|
||||||
|
},
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const getSubscriptions = () => {
|
const getSubscriptions = () => {
|
||||||
|
@ -438,6 +536,13 @@ const Subscription = ({ headerStyle, isPhone, theme }: any) => {
|
||||||
handleCancel={handleCancel}
|
handleCancel={handleCancel}
|
||||||
subscription={editedSubscription}
|
subscription={editedSubscription}
|
||||||
/>
|
/>
|
||||||
|
<SubscriptionLogModal
|
||||||
|
visible={isLogModalVisible}
|
||||||
|
handleCancel={() => {
|
||||||
|
setIsLogModalVisible(false);
|
||||||
|
}}
|
||||||
|
cron={logSubscription}
|
||||||
|
/>
|
||||||
</PageContainer>
|
</PageContainer>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
158
src/pages/subscription/logModal.tsx
Normal file
158
src/pages/subscription/logModal.tsx
Normal file
|
@ -0,0 +1,158 @@
|
||||||
|
import React, { useEffect, useState } from 'react';
|
||||||
|
import { Modal, message, Input, Form, Statistic, Button } from 'antd';
|
||||||
|
import { request } from '@/utils/http';
|
||||||
|
import config from '@/utils/config';
|
||||||
|
import {
|
||||||
|
Loading3QuartersOutlined,
|
||||||
|
CheckCircleOutlined,
|
||||||
|
} from '@ant-design/icons';
|
||||||
|
import { PageLoading } from '@ant-design/pro-layout';
|
||||||
|
|
||||||
|
enum CrontabStatus {
|
||||||
|
'running',
|
||||||
|
'idle',
|
||||||
|
'disabled',
|
||||||
|
'queued',
|
||||||
|
}
|
||||||
|
const { Countdown } = Statistic;
|
||||||
|
|
||||||
|
const SubscriptionLogModal = ({
|
||||||
|
cron,
|
||||||
|
handleCancel,
|
||||||
|
visible,
|
||||||
|
data,
|
||||||
|
logUrl,
|
||||||
|
}: {
|
||||||
|
cron?: any;
|
||||||
|
visible: boolean;
|
||||||
|
handleCancel: () => void;
|
||||||
|
data?: string;
|
||||||
|
logUrl?: string;
|
||||||
|
}) => {
|
||||||
|
const [value, setValue] = useState<string>('启动中...');
|
||||||
|
const [loading, setLoading] = useState<any>(true);
|
||||||
|
const [executing, setExecuting] = useState<any>(true);
|
||||||
|
const [isPhone, setIsPhone] = useState(false);
|
||||||
|
const [theme, setTheme] = useState<string>('');
|
||||||
|
|
||||||
|
const getCronLog = (isFirst?: boolean) => {
|
||||||
|
if (isFirst) {
|
||||||
|
setLoading(true);
|
||||||
|
}
|
||||||
|
request
|
||||||
|
.get(logUrl ? logUrl : `${config.apiPrefix}crons/${cron.id}/log`)
|
||||||
|
.then((data: any) => {
|
||||||
|
if (localStorage.getItem('logCron') === String(cron.id)) {
|
||||||
|
const log = data.data as string;
|
||||||
|
setValue(log || '暂无日志');
|
||||||
|
setExecuting(
|
||||||
|
log && !log.includes('执行结束') && !log.includes('重启面板'),
|
||||||
|
);
|
||||||
|
if (log && !log.includes('执行结束') && !log.includes('重启面板')) {
|
||||||
|
setTimeout(() => {
|
||||||
|
getCronLog();
|
||||||
|
}, 2000);
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
log &&
|
||||||
|
log.includes('重启面板') &&
|
||||||
|
cron.status === CrontabStatus.running
|
||||||
|
) {
|
||||||
|
message.warning({
|
||||||
|
content: (
|
||||||
|
<span>
|
||||||
|
系统将在
|
||||||
|
<Countdown
|
||||||
|
className="inline-countdown"
|
||||||
|
format="ss"
|
||||||
|
value={Date.now() + 1000 * 30}
|
||||||
|
/>
|
||||||
|
秒后自动刷新
|
||||||
|
</span>
|
||||||
|
),
|
||||||
|
duration: 10,
|
||||||
|
});
|
||||||
|
setTimeout(() => {
|
||||||
|
window.location.reload();
|
||||||
|
}, 30000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
if (isFirst) {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const cancel = () => {
|
||||||
|
localStorage.removeItem('logCron');
|
||||||
|
handleCancel();
|
||||||
|
};
|
||||||
|
|
||||||
|
const titleElement = () => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{(executing || loading) && <Loading3QuartersOutlined spin />}
|
||||||
|
{!executing && !loading && <CheckCircleOutlined />}
|
||||||
|
<span style={{ marginLeft: 5 }}>日志-{cron && cron.name}</span>{' '}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (cron && cron.id && visible) {
|
||||||
|
getCronLog(true);
|
||||||
|
}
|
||||||
|
}, [cron, visible]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (data) {
|
||||||
|
setValue(data);
|
||||||
|
}
|
||||||
|
}, [data]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setIsPhone(document.body.clientWidth < 768);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
title={titleElement()}
|
||||||
|
visible={visible}
|
||||||
|
centered
|
||||||
|
className="log-modal"
|
||||||
|
bodyStyle={{
|
||||||
|
minHeight: '300px',
|
||||||
|
}}
|
||||||
|
forceRender
|
||||||
|
onOk={() => cancel()}
|
||||||
|
onCancel={() => cancel()}
|
||||||
|
footer={[
|
||||||
|
<Button type="primary" onClick={() => cancel()}>
|
||||||
|
知道了
|
||||||
|
</Button>,
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
{loading ? (
|
||||||
|
<PageLoading />
|
||||||
|
) : (
|
||||||
|
<pre
|
||||||
|
style={
|
||||||
|
isPhone
|
||||||
|
? {
|
||||||
|
fontFamily: 'Source Code Pro',
|
||||||
|
width: 375,
|
||||||
|
zoom: 0.83,
|
||||||
|
}
|
||||||
|
: {}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{value}
|
||||||
|
</pre>
|
||||||
|
)}
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SubscriptionLogModal;
|
|
@ -3,7 +3,6 @@ import { Modal, message, InputNumber, Form, Radio, Select, Input } from 'antd';
|
||||||
import { request } from '@/utils/http';
|
import { request } from '@/utils/http';
|
||||||
import config from '@/utils/config';
|
import config from '@/utils/config';
|
||||||
import cron_parser from 'cron-parser';
|
import cron_parser from 'cron-parser';
|
||||||
import EditableTagGroup from '@/components/tag';
|
|
||||||
|
|
||||||
const { Option } = Select;
|
const { Option } = Select;
|
||||||
const repoUrlRegx = /[^\/\:]+\/[^\/]+(?=\.git)/;
|
const repoUrlRegx = /[^\/\:]+\/[^\/]+(?=\.git)/;
|
||||||
|
@ -39,9 +38,7 @@ const SubscriptionModal = ({
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
if (code === 200) {
|
if (code === 200) {
|
||||||
message.success(
|
message.success(subscription ? '更新订阅成功' : '新建订阅成功');
|
||||||
subscription ? '更新Subscription成功' : '新建Subscription成功',
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
message.error(data);
|
message.error(data);
|
||||||
}
|
}
|
||||||
|
@ -108,21 +105,18 @@ const SubscriptionModal = ({
|
||||||
const [intervalNumber, setIntervalNumber] = useState<number>();
|
const [intervalNumber, setIntervalNumber] = useState<number>();
|
||||||
const intervalTypeChange = (e) => {
|
const intervalTypeChange = (e) => {
|
||||||
setIntervalType(e.target.value);
|
setIntervalType(e.target.value);
|
||||||
onChange?.({ [e.target.value]: intervalNumber });
|
onChange?.({ type: e.target.value, value: intervalNumber });
|
||||||
};
|
};
|
||||||
|
|
||||||
const numberChange = (value: number) => {
|
const numberChange = (value: number) => {
|
||||||
setIntervalNumber(value);
|
setIntervalNumber(value);
|
||||||
onChange?.({ [intervalType]: value });
|
onChange?.({ type: intervalType, value });
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (value) {
|
if (value) {
|
||||||
const key = Object.keys(value)[0];
|
setIntervalType(value.type);
|
||||||
if (key) {
|
setIntervalNumber(value.value);
|
||||||
setIntervalType(key);
|
|
||||||
setIntervalNumber(value[key]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}, [value]);
|
}, [value]);
|
||||||
return (
|
return (
|
||||||
|
@ -131,11 +125,11 @@ const SubscriptionModal = ({
|
||||||
addonBefore="每"
|
addonBefore="每"
|
||||||
precision={0}
|
precision={0}
|
||||||
min={1}
|
min={1}
|
||||||
defaultValue={intervalNumber}
|
value={intervalNumber}
|
||||||
style={{ width: 'calc(100% - 58px)' }}
|
style={{ width: 'calc(100% - 58px)' }}
|
||||||
onChange={numberChange}
|
onChange={numberChange}
|
||||||
/>
|
/>
|
||||||
<Select defaultValue={intervalType} onChange={intervalTypeChange}>
|
<Select value={intervalType} onChange={intervalTypeChange}>
|
||||||
<Option value="days">天</Option>
|
<Option value="days">天</Option>
|
||||||
<Option value="hours">时</Option>
|
<Option value="hours">时</Option>
|
||||||
<Option value="minutes">分</Option>
|
<Option value="minutes">分</Option>
|
||||||
|
@ -176,10 +170,10 @@ const SubscriptionModal = ({
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
form.resetFields();
|
form.setFieldsValue(subscription || {});
|
||||||
setType('public-repo');
|
setType((subscription && subscription.type) || 'public-repo');
|
||||||
setScheduleType('crontab');
|
setScheduleType((subscription && subscription.schedule_type) || 'crontab');
|
||||||
setPullType('ssh-key');
|
setPullType((subscription && subscription.pull_type) || 'ssh-key');
|
||||||
}, [subscription, visible]);
|
}, [subscription, visible]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -201,14 +195,9 @@ const SubscriptionModal = ({
|
||||||
onCancel={() => handleCancel()}
|
onCancel={() => handleCancel()}
|
||||||
confirmLoading={loading}
|
confirmLoading={loading}
|
||||||
>
|
>
|
||||||
<Form
|
<Form form={form} name="form_in_modal" layout="vertical">
|
||||||
form={form}
|
<Form.Item name="name" label="名称">
|
||||||
name="form_in_modal"
|
<Input placeholder="请输入订阅名" />
|
||||||
initialValues={subscription}
|
|
||||||
layout="vertical"
|
|
||||||
>
|
|
||||||
<Form.Item name="name" label="别名">
|
|
||||||
<Input placeholder="请输入订阅别名" />
|
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
name="type"
|
name="type"
|
||||||
|
@ -230,7 +219,9 @@ const SubscriptionModal = ({
|
||||||
{ pattern: type === 'file' ? fileUrlRegx : repoUrlRegx },
|
{ pattern: type === 'file' ? fileUrlRegx : repoUrlRegx },
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<Input
|
<Input.TextArea
|
||||||
|
rows={4}
|
||||||
|
autoSize={true}
|
||||||
placeholder="请输入订阅链接"
|
placeholder="请输入订阅链接"
|
||||||
onPaste={onUrlChange}
|
onPaste={onUrlChange}
|
||||||
onChange={onUrlChange}
|
onChange={onUrlChange}
|
||||||
|
@ -281,7 +272,7 @@ const SubscriptionModal = ({
|
||||||
</Radio.Group>
|
</Radio.Group>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
name={scheduleType === 'crontab' ? 'schedule' : 'intervalSchedule'}
|
name={scheduleType === 'crontab' ? 'schedule' : 'interval_schedule'}
|
||||||
label="定时规则"
|
label="定时规则"
|
||||||
rules={[
|
rules={[
|
||||||
{ required: true },
|
{ required: true },
|
||||||
|
|
Loading…
Reference in New Issue
Block a user