mirror of
https://github.com/whyour/qinglong.git
synced 2026-08-05 16:25:04 +08:00
Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 437a849c2a | |||
| 8f7261dd50 | |||
| 76fa82c3a7 | |||
| 46c9774efa | |||
| 1bc64a23e3 | |||
| cc9a17afa3 | |||
| fcbcfc3a03 | |||
| 0144f3b131 | |||
| 518436d5ca | |||
| 1961a6b913 | |||
| 5edebdd5f3 | |||
| 48edf2c212 | |||
| 9b16dca9da | |||
| 9479c2498c | |||
| 4a18bcabe2 | |||
| 34f690c53d |
@@ -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
|
||||
|
||||
@@ -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,4 +1,4 @@
|
||||
export const LOG_END_SYMBOL = '\n ';
|
||||
export const LOG_END_SYMBOL = ' ';
|
||||
|
||||
export const TASK_COMMAND = 'task';
|
||||
export const QL_COMMAND = 'ql';
|
||||
|
||||
@@ -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 },
|
||||
},
|
||||
);
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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
@@ -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
@@ -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",
|
||||
|
||||
Generated
+24
-22
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -349,3 +349,7 @@ pre {
|
||||
white-space: break-spaces !important;
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
.virtuallist .ant-table-tbody > tr > td > div {
|
||||
white-space: unset !important;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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]);
|
||||
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -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],
|
||||
|
||||
@@ -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':
|
||||
|
||||
@@ -12,7 +12,6 @@ const RenameModal = ({
|
||||
visible: boolean;
|
||||
handleCancel: () => void;
|
||||
}) => {
|
||||
console.log(currentNode);
|
||||
const [form] = Form.useForm();
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
.inline-form-item {
|
||||
margin-bottom: 0;
|
||||
|
||||
.ant-form-item {
|
||||
display: inline-block;
|
||||
width: 50%;
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
@@ -1 +1 @@
|
||||
export const LOG_END_SYMBOL = '\n ';
|
||||
export const LOG_END_SYMBOL = ' ';
|
||||
|
||||
+38
-3
@@ -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
@@ -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/WankkoRee,https://github.com/catlair
|
||||
4. 修改资源通知逻辑
|
||||
5. 其他bug修复
|
||||
1. 订阅支持自动添加和删除任务设置
|
||||
2. 增加 ppc64le/s390x/386 镜像
|
||||
3. 修改 curl 等命令参数处理逻辑
|
||||
4. 修复移动端新建脚本
|
||||
5. 修改任务日志自动滚动逻辑
|
||||
6. 修复查看任务日志,列表位置重置
|
||||
7. 其他优化
|
||||
|
||||
Reference in New Issue
Block a user