Compare commits

..

16 Commits

Author SHA1 Message Date
whyour 437a849c2a 更新版本 v2.15.7 2023-02-12 17:09:07 +08:00
whyour 8f7261dd50 修复 workflow 2023-02-12 17:08:47 +08:00
whyour 76fa82c3a7 订阅支持自动添加和删除任务设置 2023-02-09 00:20:28 +08:00
whyour 46c9774efa 更新 workflow 2023-02-07 21:52:24 +08:00
whyour 1bc64a23e3 修复查看任务日志,列表位置重置 2023-02-07 21:29:56 +08:00
whyour cc9a17afa3 修复定时任务列表格式化 crontab 2023-02-06 23:26:59 +08:00
whyour fcbcfc3a03 更新 sqlite3 2023-02-06 16:24:31 +08:00
whyour 0144f3b131 修改任务日志自动滚动逻辑 2023-02-05 20:57:37 +08:00
whyour 518436d5ca 修复脚本管理跳转逻辑 2023-02-04 00:20:44 +08:00
whyour 1961a6b913 修复定时任务表格自动换行 2023-02-03 23:45:55 +08:00
whyour 5edebdd5f3 修复创建日志目录逻辑 2023-02-03 23:30:02 +08:00
whyour 48edf2c212 增加 ppc64le/s390x/386 镜像 2023-02-03 22:38:00 +08:00
whyour 9b16dca9da 修改定时任务状态宽度 2023-02-02 23:39:18 +08:00
whyour 9479c2498c 移除 console 2023-02-02 13:20:47 +08:00
whyour 4a18bcabe2 修复移动端新建脚本 2023-02-02 13:17:00 +08:00
whyour 34f690c53d 修改 shell 参数处理逻辑 2023-02-02 12:14:35 +08:00
24 changed files with 266 additions and 110 deletions
+4 -4
View File
@@ -9,9 +9,7 @@ on:
- 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10
schedule:
# 参考 https://jasonet.co/posts/scheduled-actions/
# note: 这里是GMT时间,北京时间减去八小时即可。如北京时间 22:30 => GMT 14:30
# minute hour day month dayOfWeek
- cron: '00 14 * * *' # GMT 14:00 => 北京时间 22:00
- cron: '00 14 * * *' # GMT 15:00 => 北京时间 23:00
workflow_dispatch:
jobs:
@@ -87,6 +85,8 @@ jobs:
git push --force --quiet gitlab ${GITHUB_BRANCH}:${GITHUB_BRANCH}
build:
if: github.ref != 'refs/heads/master'
needs: build-static
runs-on: ubuntu-latest
@@ -155,7 +155,7 @@ jobs:
QL_BRANCH=${{ github.ref_name }}
SOURCE_COMMIT=${{ github.sha }}
network: host
platforms: linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64
platforms: linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64,linux/ppc64le,linux/s390x,linux/386
context: .
file: ./docker/Dockerfile
push: true
+4
View File
@@ -50,6 +50,8 @@ export default (app: Router) => {
schedule_type: Joi.string().required(),
alias: Joi.string().required(),
proxy: Joi.string().optional().allow('').allow(null),
autoAddCron: Joi.boolean().optional().allow('').allow(null),
autoDelCron: Joi.boolean().optional().allow('').allow(null),
}),
}),
async (req: Request, res: Response, next: NextFunction) => {
@@ -179,6 +181,8 @@ export default (app: Router) => {
sub_after: Joi.string().optional().allow('').allow(null),
alias: Joi.string().required(),
proxy: Joi.string().optional().allow('').allow(null),
autoAddCron: Joi.boolean().optional().allow('').allow(null),
autoDelCron: Joi.boolean().optional().allow('').allow(null),
id: Joi.number().required(),
}),
}),
+1 -1
View File
@@ -1,4 +1,4 @@
export const LOG_END_SYMBOL = '\n          ';
export const LOG_END_SYMBOL = '     ';
export const TASK_COMMAND = 'task';
export const QL_COMMAND = 'ql';
+6
View File
@@ -29,6 +29,8 @@ export class Subscription {
sub_before?: string;
sub_after?: string;
proxy?: string;
autoAddCron?: 1 | 0;
autoDelCron?: 1 | 0;
constructor(options: Subscription) {
this.id = options.id;
@@ -56,6 +58,8 @@ export class Subscription {
this.sub_before = options.sub_before;
this.sub_after = options.sub_after;
this.proxy = options.proxy;
this.autoAddCron = options.autoAddCron ? 1 : 0;
this.autoDelCron = options.autoDelCron ? 1 : 0;
}
}
@@ -105,5 +109,7 @@ export const SubscriptionModel = sequelize.define<SubscriptionInstance>(
schedule_type: DataTypes.STRING,
alias: { type: DataTypes.STRING, unique: 'alias' },
proxy: { type: DataTypes.STRING, allowNull: true },
autoAddCron: { type: DataTypes.NUMBER, allowNull: true },
autoDelCron: { type: DataTypes.NUMBER, allowNull: true },
},
);
+10
View File
@@ -36,6 +36,16 @@ export default async () => {
try {
await sequelize.query('alter table CrontabViews add column type NUMBER');
} catch (error) {}
try {
await sequelize.query(
'alter table Subscriptions add column autoAddCron NUMBER',
);
} catch (error) {}
try {
await sequelize.query(
'alter table Subscriptions add column autoDelCron NUMBER',
);
} catch (error) {}
// 2.10-2.11 升级
const cronDbFile = path.join(config.rootPath, 'db/crontab.db');
+7 -3
View File
@@ -13,7 +13,6 @@ import {
spawn,
} from 'child_process';
import fs from 'fs';
import cron_parser from 'cron-parser';
import {
getFileContentByName,
concurrentRun,
@@ -85,13 +84,17 @@ export default class SubscriptionService {
branch,
extensions,
proxy,
autoAddCron,
autoDelCron,
} = doc;
if (type === 'file') {
command += `raw "${_url}"`;
} else {
command += `repo "${_url}" "${whitelist || ''}" "${blacklist || ''}" "${
dependences || ''
}" "${branch || ''}" "${extensions || ''}" "${proxy || ''}"`;
}" "${branch || ''}" "${extensions || ''}" "${proxy || ''}" "${
Boolean(autoAddCron) || ''
}" "${Boolean(autoDelCron) || ''}"`;
}
return command;
}
@@ -281,7 +284,8 @@ export default class SubscriptionService {
}
public async update(payload: Subscription): Promise<Subscription> {
const newDoc = await this.updateDb(payload);
const tab = new Subscription(payload);
const newDoc = await this.updateDb(tab);
await this.handleTask(newDoc, !newDoc.is_disabled);
return newDoc;
}
+1 -1
View File
@@ -2,7 +2,7 @@ FROM python:3.10-alpine as builder
COPY package.json .npmrc pnpm-lock.yaml /tmp/build/
RUN set -x \
&& apk update \
&& apk add nodejs npm \
&& apk add nodejs npm git \
&& npm i -g pnpm \
&& cd /tmp/build \
&& pnpm install --prod
+1 -1
View File
@@ -82,7 +82,7 @@
"sequelize": "^6.25.5",
"serve-handler": "^6.1.3",
"sockjs": "^0.3.24",
"sqlite3": "npm:@louislam/sqlite3@15.1.2",
"sqlite3": "git+https://github.com/whyour/node-sqlite3.git#v1.0.2",
"toad-scheduler": "^1.6.0",
"typedi": "^0.10.0",
"uuid": "^8.3.2",
+24 -22
View File
@@ -80,7 +80,7 @@ specifiers:
serve-handler: ^6.1.3
sockjs: ^0.3.24
sockjs-client: ^1.6.0
sqlite3: npm:@louislam/sqlite3@15.1.2
sqlite3: git+https://github.com/whyour/node-sqlite3.git#v1.0.2
toad-scheduler: ^1.6.0
ts-node: ^10.6.0
tslib: ^2.4.0
@@ -122,10 +122,10 @@ dependencies:
nodemailer: 6.8.0
pstree.remy: 1.1.8
reflect-metadata: 0.1.13
sequelize: 6.28.0_@louislam+sqlite3@15.1.2
sequelize: 6.28.0_@whyour+sqlite3@1.0.2
serve-handler: 6.1.5
sockjs: 0.3.24
sqlite3: /@louislam/sqlite3/15.1.2
sqlite3: github.com/whyour/node-sqlite3/7a5a5727ec663887b82050844f9564cf35961015
toad-scheduler: 1.6.1
typedi: 0.10.0
uuid: 8.3.2
@@ -3171,23 +3171,6 @@ packages:
react-is: 16.13.1
dev: true
/@louislam/sqlite3/15.1.2:
resolution: {integrity: sha512-VRquWrCKKwfOnzwVh6hOud8lHPvv2R7Jic3gyZCL5kiZpNfmJ71DLCV9SNgLaMDloU+mVWymLev8vehlf7xf5g==}
requiresBuild: true
peerDependenciesMeta:
node-gyp:
optional: true
dependencies:
'@mapbox/node-pre-gyp': 1.0.10
node-addon-api: 4.3.0
tar: 6.1.13
optionalDependencies:
node-gyp: 7.1.2
transitivePeerDependencies:
- encoding
- supports-color
dev: false
/@mapbox/node-pre-gyp/1.0.10:
resolution: {integrity: sha512-4ySo4CjzStuprMwk35H5pPbkymjv1SF3jGLj6rAHp/xT/RF7TL7bd9CTm1xDY49K2qF7jmR/g7k+SkLETP6opA==}
hasBin: true
@@ -13360,7 +13343,7 @@ packages:
engines: {node: '>= 10.0.0'}
dev: false
/sequelize/6.28.0_@louislam+sqlite3@15.1.2:
/sequelize/6.28.0_@whyour+sqlite3@1.0.2:
resolution: {integrity: sha512-+WHqvUQgTp19GLkt+gyQ+F6qg+FIEO2O5F9C0TOYV/PjZ2a/XwWvVkL1NCkS4VSIjVVvAUutiW6Wv9ofveGaVw==}
engines: {node: '>=10.0.0'}
peerDependencies:
@@ -13405,7 +13388,7 @@ packages:
retry-as-promised: 7.0.3
semver: 7.3.8
sequelize-pool: 7.1.0
sqlite3: /@louislam/sqlite3/15.1.2
sqlite3: github.com/whyour/node-sqlite3/7a5a5727ec663887b82050844f9564cf35961015
toposort-class: 1.0.1
uuid: 8.3.2
validator: 13.7.0
@@ -15235,3 +15218,22 @@ packages:
normalize-path: 1.0.0
strip-indent: 2.0.0
dev: true
github.com/whyour/node-sqlite3/7a5a5727ec663887b82050844f9564cf35961015:
resolution: {tarball: https://codeload.github.com/whyour/node-sqlite3/tar.gz/7a5a5727ec663887b82050844f9564cf35961015}
name: '@whyour/sqlite3'
version: 1.0.2
requiresBuild: true
peerDependenciesMeta:
node-gyp:
optional: true
dependencies:
'@mapbox/node-pre-gyp': 1.0.10
node-addon-api: 4.3.0
tar: 6.1.13
optionalDependencies:
node-gyp: 7.1.2
transitivePeerDependencies:
- encoding
- supports-color
dev: false
+1 -2
View File
@@ -124,8 +124,7 @@ handle_task_after() {
local end_timestamp=$(format_timestamp "$time_format" "$etime")
local diff_time=$(($end_timestamp - $begin_timestamp))
echo -e "\n\n## 执行结束... $end_time 耗时 $diff_time"
echo -e "\n     "
echo -e "\n\n## 执行结束... $end_time 耗时 $diff_time     "
[[ $ID ]] && update_cron "\"$ID\"" "1" "" "$log_path" "$begin_timestamp" "$diff_time"
}
+7 -4
View File
@@ -48,8 +48,11 @@ handle_log_path() {
log_dir="${log_dir_tmp%.*}${suffix}"
log_path="$log_dir/$log_time.log"
cmd=">> $dir_log/$log_path 2>&1"
[[ "$show_log" == "true" ]] && cmd=""
make_dir "$dir_log/$log_dir"
if [[ "$show_log" == "true" ]]; then
cmd=""
else
make_dir "$dir_log/$log_dir"
fi
}
format_params() {
@@ -58,7 +61,7 @@ format_params() {
if type timeout &>/dev/null; then
timeoutCmd="timeout --foreground -s 14 -k 10s $command_timeout_time "
fi
params=$(echo "$@" | sed -E 's/([^ ])&([^ ])/\1\\\&\2/g')
# params=$(echo "$@" | sed -E 's/([^ ])&([^ ])/\1\\\&\2/g')
}
while getopts ":lm:" opt; do
@@ -81,7 +84,7 @@ format_params "$@"
define_program "$@"
handle_log_path "$@"
eval . $dir_shell/otask.sh "$params" "$cmd"
eval . $dir_shell/otask.sh "$cmd"
[[ -f "$dir_log/$log_path" ]] && cat "$dir_log/$log_path"
exit 0
+26 -13
View File
@@ -118,6 +118,8 @@ update_repo() {
local branch="$5"
local extensions="$6"
local proxy="$7"
local autoAddCron="$8"
local autoDelCron="$9"
local tmp="${url%/*}"
local authorTmp1="${tmp##*/}"
local authorTmp2="${authorTmp1##*:}"
@@ -137,7 +139,7 @@ update_repo() {
fi
if [[ $exit_status -eq 0 ]]; then
echo -e "\n更新${repo_path}成功...\n"
diff_scripts "$repo_path" "$author" "$path" "$blackword" "$dependence" "$extensions"
diff_scripts "$repo_path" "$author" "$path" "$blackword" "$dependence" "$extensions" "$autoAddCron" "$autoDelCron"
else
echo -e "\n更新${repo_path}失败,请检查网络...\n"
fi
@@ -288,6 +290,15 @@ diff_scripts() {
local blackword="$4"
local dependence="$5"
local extensions="$6"
local autoAddCron="$7"
local autoDelCron="$8"
if [[ ! $autoAddCron ]];then
autoAddCron=${AutoAddCron}
fi
if [[ ! $autoDelCron ]];then
autoDelCron=${AutoDelCron}
fi
gen_list_repo "$repo_path" "$author" "$path" "$blackword" "$dependence" "$extensions"
@@ -297,13 +308,13 @@ diff_scripts() {
if [[ -s $list_drop ]]; then
output_list_add_drop $list_drop "失效"
if [[ ${AutoDelCron} == true ]]; then
if [[ ${autoDelCron} == true ]]; then
del_cron $list_drop $uniq_path
fi
fi
if [[ -s $list_add ]]; then
output_list_add_drop $list_add "新"
if [[ ${AutoAddCron} == true ]]; then
if [[ ${autoAddCron} == true ]]; then
add_cron $list_add $uniq_path
fi
fi
@@ -404,13 +415,16 @@ main() {
done
[[ "$show_log" == "true" ]] && shift $(($OPTIND - 1))
local p1=$1
local p2=$2
local p3=$3
local p4=$4
local p5=$5
local p6=$6
local p7=$7
local p1="${1}"
local p2="${2}"
local p3="${3}"
local p4="${4}"
local p5="${5}"
local p6="${6}"
local p7="${7}"
local p8="${8}"
local p9="${9}"
local p10="${10}"
local log_dir="${p1}"
make_dir "$dir_log/$log_dir"
local log_time=$(date "+%Y-%m-%d-%H-%M-%S")
@@ -452,7 +466,7 @@ main() {
repo)
get_uniq_path "$p2" "$p6"
if [[ -n $p2 ]]; then
update_repo "$p2" "$p3" "$p4" "$p5" "$p6" "$p7" "$p8"
update_repo "$p2" "$p3" "$p4" "$p5" "$p6" "$p7" "$p8" "$p9" "$p10"
else
eval echo -e "命令输入错误...\\\n" $cmd
eval usage $cmd
@@ -499,8 +513,7 @@ main() {
[[ $ID ]] && update_cron "\"$ID\"" "1" "" "$log_path" "$begin_timestamp" "$diff_time"
if [[ "$p1" != "repo" ]] && [[ "$p1" != "raw" ]]; then
eval echo -e "\\\n\#\# 执行结束... $end_time 耗时 $diff_time" $cmd
eval echo -e "\\\n          " $cmd
eval echo -e "\\\n\#\# 执行结束... $end_time 耗时 $diff_time     " $cmd
fi
if [[ -f $file_path ]]; then
+4
View File
@@ -349,3 +349,7 @@ pre {
white-space: break-spaces !important;
padding: 0 !important;
}
.virtuallist .ant-table-tbody > tr > td > div {
white-space: unset !important;
}
+12
View File
@@ -179,3 +179,15 @@ tr.drop-over-upward td {
margin-left: 40px;
}
}
.log-modal {
.log-container {
overflow-y: auto;
min-height: 300px;
max-height: calc(80vh - 110px);
max-height: calc(80vh - var(--vh-offset, 110px));
padding: 24px;
margin: -24px;
}
}
+5 -11
View File
@@ -51,7 +51,7 @@ import ViewManageModal from './viewManageModal';
import { FilterValue, SorterResult } from 'antd/lib/table/interface';
import { SharedContext } from '@/layouts';
import useTableScrollHeight from '@/hooks/useTableScrollHeight';
import { getCommandScript } from '@/utils';
import { getCommandScript, parseCrontab } from '@/utils';
import { ColumnProps } from 'antd/lib/table';
import { VList } from 'virtuallist-antd';
@@ -263,13 +263,12 @@ const Crontab = () => {
.replace(' 24:', ' 00:');
},
},
{
title: '状态',
key: 'status',
dataIndex: 'status',
align: 'center' as const,
width: 85,
width: 88,
filters: [
{
text: '运行中',
@@ -447,10 +446,7 @@ const Crontab = () => {
data.map((x) => {
return {
...x,
nextRunTime: cron_parser
.parseExpression(x.schedule)
.next()
.toDate(),
nextRunTime: parseCrontab(x.schedule),
};
}),
);
@@ -737,10 +733,7 @@ const Crontab = () => {
if (code === 200) {
const index = value.findIndex((x) => x.id === cron.id);
const result = [...value];
data.nextRunTime = cron_parser
.parseExpression(data.schedule)
.next()
.toDate();
data.nextRunTime = parseCrontab(data.schedule);
if (index !== -1) {
result.splice(index, 1, {
...cron,
@@ -931,6 +924,7 @@ const Crontab = () => {
const vComponents = useMemo(() => {
return VList({
height: tableScrollHeight!,
resetTopWhenDataChange: false,
});
}, [tableScrollHeight]);
+52 -25
View File
@@ -34,6 +34,7 @@ const CronLogModal = ({
const [loading, setLoading] = useState<any>(true);
const [executing, setExecuting] = useState<any>(true);
const [isPhone, setIsPhone] = useState(false);
const scrollInfoRef = useRef({ value: 0, down: true });
const getCronLog = (isFirst?: boolean) => {
if (isFirst) {
@@ -44,17 +45,19 @@ const CronLogModal = ({
.then(({ code, data }) => {
if (
code === 200 &&
localStorage.getItem('logCron') === String(cron.id)
localStorage.getItem('logCron') === String(cron.id) &&
data !== value
) {
const log = data as string;
setValue(log || '暂无日志');
const hasNext = log && !logEnded(log) && !log.includes('重启面板') && !log.includes('任务未运行或运行失败,请尝试手动运行');
const hasNext = Boolean(
log &&
!logEnded(log) &&
!log.includes('重启面板') &&
!log.includes('任务未运行或运行失败,请尝试手动运行'),
);
setExecuting(hasNext);
setTimeout(() => {
document
.querySelector('#log-flag')!
.scrollIntoView({ behavior: 'smooth' });
}, 1000);
autoScroll();
if (hasNext) {
setTimeout(() => {
getCronLog();
@@ -92,11 +95,33 @@ const CronLogModal = ({
});
};
const autoScroll = () => {
if (!scrollInfoRef.current.down) {
return;
}
setTimeout(() => {
document
.querySelector('#log-flag')!
.scrollIntoView({ behavior: 'smooth' });
}, 1000);
};
const cancel = () => {
localStorage.removeItem('logCron');
handleCancel();
};
const handleScroll = (e) => {
const sTop = e.target.scrollTop;
if (scrollInfoRef.current.down) {
scrollInfoRef.current = {
value: sTop,
down: sTop > scrollInfoRef.current.value,
};
}
};
const titleElement = () => {
return (
<>
@@ -141,24 +166,26 @@ const CronLogModal = ({
</Button>,
]}
>
{loading ? (
<PageLoading />
) : (
<pre
style={
isPhone
? {
fontFamily: 'Source Code Pro',
width: 375,
zoom: 0.83,
}
: {}
}
>
{value}
</pre>
)}
<div id="log-flag"></div>
<div onScroll={handleScroll} className="log-container">
{loading ? (
<PageLoading />
) : (
<pre
style={
isPhone
? {
fontFamily: 'Source Code Pro',
width: 375,
zoom: 0.83,
}
: {}
}
>
{value}
</pre>
)}
<div id="log-flag"></div>
</div>
</Modal>
);
};
-1
View File
@@ -167,7 +167,6 @@ const ViewManageModal = ({
};
const onShowChange = (checked: boolean, record: any, index: number) => {
console.log(checked);
request
.put(`${config.apiPrefix}crons/views/${checked ? 'enable' : 'disable'}`, {
data: [record.id],
+9 -6
View File
@@ -36,7 +36,7 @@ import EditScriptNameModal from './editNameModal';
import debounce from 'lodash/debounce';
import { history, useOutletContext, useLocation } from '@umijs/max';
import { parse } from 'query-string';
import { depthFirstSearch } from '@/utils';
import { depthFirstSearch, findNode } from '@/utils';
import { SharedContext } from '@/layouts';
import useFilterTreeData from '@/hooks/useFilterTreeData';
import uniq from 'lodash/uniq';
@@ -80,7 +80,7 @@ const Script = () => {
if (code === 200) {
setData(data);
initState();
initGetScript();
initGetScript(data);
}
})
.finally(() => needLoading && setLoading(false));
@@ -100,7 +100,7 @@ const Script = () => {
});
};
const initGetScript = () => {
const initGetScript = (_data: any) => {
const { p, s } = parse(history.location.search);
if (s) {
const vkey = `${p}/${s}`;
@@ -111,8 +111,11 @@ const Script = () => {
parent: p,
},
};
setExpandedKeys([p]);
onTreeSelect([vkey], obj);
const item = findNode(_data, (c) => c.key === obj.node.key);
if (item) {
setExpandedKeys([p as string]);
onTreeSelect([vkey], obj);
}
}
};
@@ -387,7 +390,7 @@ const Script = () => {
const menuAction = (key: string | number) => {
switch (key) {
case 'save':
case 'add':
addFile();
break;
case 'edit':
-1
View File
@@ -12,7 +12,6 @@ const RenameModal = ({
visible: boolean;
handleCancel: () => void;
}) => {
console.log(currentNode);
const [form] = Form.useForm();
const [loading, setLoading] = useState(false);
+9
View File
@@ -0,0 +1,9 @@
.inline-form-item {
margin-bottom: 0;
.ant-form-item {
display: inline-block;
width: 50%;
margin-bottom: 0px;
}
}
+35 -4
View File
@@ -1,5 +1,14 @@
import React, { useCallback, useEffect, useState } from 'react';
import { Modal, message, InputNumber, Form, Radio, Select, Input } from 'antd';
import {
Modal,
message,
InputNumber,
Form,
Radio,
Select,
Input,
Switch,
} from 'antd';
import { request } from '@/utils/http';
import config from '@/utils/config';
import cron_parser from 'cron-parser';
@@ -26,7 +35,11 @@ const SubscriptionModal = ({
const handleOk = async (values: any) => {
setLoading(true);
const method = subscription ? 'put' : 'post';
const payload = { ...values };
const payload = {
...values,
autoAddCron: Boolean(values.autoAddCron),
autoDelCron: Boolean(values.autoDelCron),
};
if (subscription) {
payload.id = subscription.id;
}
@@ -110,8 +123,8 @@ const SubscriptionModal = ({
}
};
const numberChange = (value: number) => {
setIntervalNumber(value);
const numberChange = (value: number | null) => {
setIntervalNumber(value || 1);
if (!value) {
onChange?.(null);
} else {
@@ -452,6 +465,24 @@ const SubscriptionModal = ({
}
/>
</Form.Item>
<Form.Item style={{ marginBottom: 0 }} className="inline-form-item">
<Form.Item
name="autoAddCron"
label="自动添加任务"
valuePropName="checked"
initialValue={true}
>
<Switch />
</Form.Item>
<Form.Item
name="autoDelCron"
label="自动删除任务"
valuePropName="checked"
initialValue={true}
>
<Switch />
</Form.Item>
</Form.Item>
</Form>
</Modal>
);
+1 -1
View File
@@ -1 +1 @@
export const LOG_END_SYMBOL = '\n          ';
export const LOG_END_SYMBOL = '     ';
+38 -3
View File
@@ -1,4 +1,5 @@
import { LOG_END_SYMBOL } from './const';
import cron_parser from 'cron-parser';
export default function browserType() {
// 权重:系统 + 系统版本 > 平台 > 内核 + 载体 + 内核版本 + 载体版本 > 外壳 + 外壳版本
@@ -152,9 +153,9 @@ export default function browserType() {
shell === 'none'
? {}
: {
shell, // wechat qq uc 360 2345 sougou liebao maxthon
shellVs,
},
shell, // wechat qq uc 360 2345 sougou liebao maxthon
shellVs,
},
);
console.log(
@@ -272,6 +273,29 @@ export function depthFirstSearch<
return c;
}
export function findNode<T extends Record<string, any> & { children?: T[] }>(
children: T[],
condition: (column: T) => boolean,
) {
const c = [...children];
let item;
function find(cls: T[] | undefined): T | undefined {
if (!cls) return;
for (let i = 0; i < cls?.length; i++) {
if (condition(cls[i])) {
item = cls[i];
} else if (cls[i].children) {
find(cls[i].children);
}
}
}
find(c);
return item;
}
export function logEnded(log: string): boolean {
const endTips = [LOG_END_SYMBOL, '执行结束'];
return endTips.some((x) => log.includes(x));
@@ -303,3 +327,14 @@ export function getCommandScript(
}
return [s, p];
}
export function parseCrontab(schedule: string): Date {
try {
const time = cron_parser.parseExpression(schedule);
if (time) {
return time.next().toDate();
}
} catch (error) {}
return new Date('1970');
}
+9 -7
View File
@@ -1,8 +1,10 @@
version: 2.15.6
changeLogLink: https://t.me/jiao_long/358
version: 2.15.7
changeLogLink: https://t.me/jiao_long/363
changeLog: |
1. 修复定时任务数据量大时卡顿,丝滑滚动
2. 修复查看运行中任务日志时自动向下滚动
3. 支持SMTP邮件通知,感谢 https://github.com/WankkoReehttps://github.com/catlair
4. 修改资源通知逻辑
5. 其他bug修复
1. 订阅支持自动添加和删除任务设置
2. 增加 ppc64le/s390x/386 镜像
3. 修改 curl 等命令参数处理逻辑
4. 修复移动端新建脚本
5. 修改任务日志自动滚动逻辑
6. 修复查看任务日志,列表位置重置
7. 其他优化