mirror of
https://github.com/whyour/qinglong.git
synced 2026-08-06 00:34:33 +08:00
Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1cfcf8d194 | |||
| d1e9f741d0 | |||
| 0cde9d07ee | |||
| a266425348 | |||
| 6dbe02e321 | |||
| 39979879c9 | |||
| ba3f604a5c | |||
| 9572582401 | |||
| 2bcef0ba75 | |||
| 31cd7c4007 | |||
| 1c4c1799b0 | |||
| 2d558df15e | |||
| 7b769da4e2 |
+8
-3
@@ -43,7 +43,7 @@ export default (app: Router) => {
|
||||
children.push({
|
||||
title: childFile,
|
||||
value: childFile,
|
||||
key: childFile,
|
||||
key: `${fileOrDir}-${childFile}`,
|
||||
mtime: statObj.mtimeMs,
|
||||
parent: fileOrDir,
|
||||
});
|
||||
@@ -120,6 +120,9 @@ export default (app: Router) => {
|
||||
if (!path.endsWith('/')) {
|
||||
path += '/';
|
||||
}
|
||||
if (!path.startsWith('/')) {
|
||||
path = `${config.scriptPath}${path}`;
|
||||
}
|
||||
if (config.writePathList.every((x) => !path.startsWith(x))) {
|
||||
return res.send({
|
||||
code: 430,
|
||||
@@ -184,15 +187,17 @@ export default (app: Router) => {
|
||||
celebrate({
|
||||
body: Joi.object({
|
||||
filename: Joi.string().required(),
|
||||
path: Joi.string().allow(''),
|
||||
}),
|
||||
}),
|
||||
async (req: Request, res: Response, next: NextFunction) => {
|
||||
const logger: Logger = Container.get('logger');
|
||||
try {
|
||||
let { filename } = req.body as {
|
||||
let { filename, path } = req.body as {
|
||||
filename: string;
|
||||
path: string;
|
||||
};
|
||||
const filePath = `${config.scriptPath}${filename}`;
|
||||
const filePath = `${config.scriptPath}${path}/${filename}`;
|
||||
fs.unlinkSync(filePath);
|
||||
res.send({ code: 200 });
|
||||
} catch (e) {
|
||||
|
||||
+5
-2
@@ -44,8 +44,11 @@ const run = async () => {
|
||||
};
|
||||
|
||||
app
|
||||
.listen(config.cronPort, () => {
|
||||
run();
|
||||
.listen(config.cronPort, async () => {
|
||||
await require('./loaders/sentry').default({ expressApp: app });
|
||||
await require('./loaders/db').default();
|
||||
|
||||
await run();
|
||||
Logger.info(`
|
||||
################################################
|
||||
🛡️ Schedule listening on port: ${config.cronPort} 🛡️
|
||||
|
||||
+57
-4
@@ -13,6 +13,13 @@
|
||||
const querystring = require('querystring');
|
||||
const $ = new Env();
|
||||
const timeout = 15000; //超时时间(单位毫秒)
|
||||
// =======================================gotify通知设置区域==============================================
|
||||
//gotify_url 填写gotify地址,如https://push.example.de:8080
|
||||
//gotify_token 填写gotify的消息应用token
|
||||
//gotify_priority 填写推送消息优先级,默认为0
|
||||
let GOTIFY_URL = '';
|
||||
let GOTIFY_TOKEN = '';
|
||||
let GOTIFY_PRIORITY = 0;
|
||||
// =======================================go-cqhttp通知设置区域===========================================
|
||||
//gobot_url 填写请求地址http://127.0.0.1/send_private_msg
|
||||
//gobot_token 填写在go-cqhttp文件设置的访问密钥
|
||||
@@ -84,6 +91,16 @@ let PUSH_PLUS_TOKEN = '';
|
||||
let PUSH_PLUS_USER = '';
|
||||
|
||||
//==========================云端环境变量的判断与接收=========================
|
||||
if (process.env.GOTIFY_URL) {
|
||||
GOTIFY_URL = process.env.GOTIFY_URL;
|
||||
}
|
||||
if (process.env.GOTIFY_TOKEN) {
|
||||
GOTIFY_TOKEN = process.env.GOTIFY_TOKEN;
|
||||
}
|
||||
if (process.env.GOTIFY_PRIORITY) {
|
||||
GOTIFY_PRIORITY = process.env.GOTIFY_PRIORITY;
|
||||
}
|
||||
|
||||
if (process.env.GOBOT_URL) {
|
||||
GOBOT_URL = process.env.GOBOT_URL;
|
||||
}
|
||||
@@ -200,9 +217,45 @@ async function sendNotify(
|
||||
qywxamNotify(text, desp), //企业微信应用消息推送
|
||||
iGotNotify(text, desp, params), //iGot
|
||||
gobotNotify(text, desp),//go-cqhttp
|
||||
gotifyNotify(text, desp),//gotify
|
||||
]);
|
||||
}
|
||||
|
||||
function gotifyNotify(text, desp) {
|
||||
return new Promise((resolve) => {
|
||||
if (GOTIFY_URL && GOTIFY_TOKEN) {
|
||||
const options = {
|
||||
url: `${GOTIFY_URL}/message?token=${GOTIFY_TOKEN}`,
|
||||
body: `title=${encodeURIComponent(text)}&message=${encodeURIComponent(desp)}&priority=${GOTIFY_PRIORITY}`,
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
}
|
||||
};
|
||||
$.post(options, (err, resp, data) => {
|
||||
try {
|
||||
if (err) {
|
||||
console.log('gotify发送通知调用API失败!!\n');
|
||||
console.log(err);
|
||||
} else {
|
||||
data = JSON.parse(data);
|
||||
if (data.id) {
|
||||
console.log('gotify发送通知消息成功🎉\n');
|
||||
} else {
|
||||
console.log(`${data.message}\n`);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
$.logErr(e, resp);
|
||||
} finally {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function gobotNotify(text, desp, time = 2100) {
|
||||
return new Promise((resolve) => {
|
||||
if (GOBOT_URL) {
|
||||
@@ -673,8 +726,8 @@ function qywxamNotify(text, desp) {
|
||||
if (err) {
|
||||
console.log(
|
||||
'成员ID:' +
|
||||
ChangeUserId(desp) +
|
||||
'企业微信应用消息发送通知消息失败!!\n',
|
||||
ChangeUserId(desp) +
|
||||
'企业微信应用消息发送通知消息失败!!\n',
|
||||
);
|
||||
console.log(err);
|
||||
} else {
|
||||
@@ -682,8 +735,8 @@ function qywxamNotify(text, desp) {
|
||||
if (data.errcode === 0) {
|
||||
console.log(
|
||||
'成员ID:' +
|
||||
ChangeUserId(desp) +
|
||||
'企业微信应用消息发送通知消息成功🎉。\n',
|
||||
ChangeUserId(desp) +
|
||||
'企业微信应用消息发送通知消息成功🎉。\n',
|
||||
);
|
||||
} else {
|
||||
console.log(`${data.errmsg}\n`);
|
||||
|
||||
@@ -51,6 +51,10 @@ push_config = {
|
||||
# /send_group_msg 时填入 group_id=QQ群
|
||||
'GOBOT_TOKEN': '', # go-cqhttp 的 access_token
|
||||
|
||||
'GOTIFY_URL': '', # gotify地址,如https://push.example.de:8080
|
||||
'GOTIFY_TOKEN': '', # gotify的消息应用token
|
||||
'GOTIFY_PRIORITY': 0, # 推送消息优先级,默认为0
|
||||
|
||||
'IGOT_PUSH_KEY': '', # iGot 聚合推送的 IGOT_PUSH_KEY
|
||||
|
||||
'PUSH_KEY': '', # server 酱的 PUSH_KEY,兼容旧版与 Turbo 版
|
||||
@@ -194,6 +198,25 @@ def go_cqhttp(title: str, content: str) -> None:
|
||||
print("go-cqhttp 推送失败!")
|
||||
|
||||
|
||||
def gotify(title:str,content:str) -> None:
|
||||
"""
|
||||
使用 gotify 推送消息。
|
||||
"""
|
||||
if not push_config.get("GOTIFY_URL") or not push_config.get("GOTIFY_TOKEN"):
|
||||
print("gotify 服务的 GOTIFY_URL 或 GOTIFY_TOKEN 未设置!!\n取消推送")
|
||||
return
|
||||
print("gotify 服务启动")
|
||||
|
||||
url = f'{push_config.get("GOTIFY_URL")}/message?token={push_config.get("GOTIFY_TOKEN")}'
|
||||
data = {"title": title,"message": content,"priority": push_config.get("GOTIFY_PRIORITY")}
|
||||
response = requests.post(url,data=data).json()
|
||||
|
||||
if response.get("id"):
|
||||
print("gotify 推送成功!")
|
||||
else:
|
||||
print("gotify 推送失败!")
|
||||
|
||||
|
||||
def iGot(title: str, content: str) -> None:
|
||||
"""
|
||||
使用 iGot 推送消息。
|
||||
@@ -473,6 +496,8 @@ if push_config.get("FSKEY"):
|
||||
notify_function.append(feishu_bot)
|
||||
if push_config.get("GOBOT_URL") and push_config.get("GOBOT_QQ"):
|
||||
notify_function.append(go_cqhttp)
|
||||
if push_config.get("GOTIFY_URL") and push_config.get("GOTIFY_TOKEN"):
|
||||
notify_function.append(gotify)
|
||||
if push_config.get("IGOT_PUSH_KEY"):
|
||||
notify_function.append(iGot)
|
||||
if push_config.get("PUSH_KEY"):
|
||||
|
||||
+1
-1
@@ -31,7 +31,7 @@ cp -f "$repo_path/jbot/requirements.txt" "$dir_root"
|
||||
cd $dir_root
|
||||
cat requirements.txt | while read LREAD
|
||||
do
|
||||
if test ! -z "$(pip3 show "${LREAD%%=*}" 1>/dev/null)"; then
|
||||
if [[ ! $(pip3 show "${LREAD%%=*}" 2>/dev/null) ]]; then
|
||||
pip3 --default-timeout=100 install ${LREAD}
|
||||
fi
|
||||
done
|
||||
|
||||
+4
-10
@@ -39,17 +39,11 @@ copy_dep() {
|
||||
reload_pm2() {
|
||||
pm2 l &>/dev/null
|
||||
|
||||
if test -z "$(pm2 info panel 1>/dev/null)"; then
|
||||
pm2 reload panel --source-map-support --time &>/dev/null
|
||||
else
|
||||
pm2 start $dir_root/build/app.js -n panel --source-map-support --time &>/dev/null
|
||||
fi
|
||||
pm2 delete panel --source-map-support --time &>/dev/null
|
||||
pm2 start $dir_root/build/app.js -n panel --source-map-support --time &>/dev/null
|
||||
|
||||
if test -z "$(pm2 info schedule 1>/dev/null)"; then
|
||||
pm2 reload schedule --source-map-support --time &>/dev/null
|
||||
else
|
||||
pm2 start $dir_root/build/schedule.js -n schedule --source-map-support --time &>/dev/null
|
||||
fi
|
||||
pm2 delete schedule --source-map-support --time &>/dev/null
|
||||
pm2 start $dir_root/build/schedule.js -n schedule --source-map-support --time &>/dev/null
|
||||
}
|
||||
|
||||
pm2_log() {
|
||||
|
||||
@@ -10,6 +10,7 @@ dir_repo=$dir_root/repo
|
||||
dir_raw=$dir_root/raw
|
||||
dir_log=$dir_root/log
|
||||
dir_db=$dir_root/db
|
||||
dir_dep=$dir_root/deps
|
||||
dir_list_tmp=$dir_log/.tmp
|
||||
dir_code=$dir_log/code
|
||||
dir_update_log=$dir_log/update
|
||||
@@ -154,6 +155,7 @@ fix_config() {
|
||||
make_dir $dir_repo
|
||||
make_dir $dir_raw
|
||||
make_dir $dir_update_log
|
||||
make_dir $dir_dep
|
||||
|
||||
if [[ ! -s $file_config_user ]]; then
|
||||
echo -e "复制一份 $file_config_sample 为 $file_config_user,随后请按注释编辑你的配置文件:$file_config_user\n"
|
||||
|
||||
+3
-3
@@ -106,7 +106,7 @@ run_normal() {
|
||||
|
||||
cd $dir_scripts
|
||||
local relative_path="${first_param%/*}"
|
||||
if [[ ! -z ${relative_path} ]]; then
|
||||
if [[ ! -z ${relative_path} ]] && [[ ${first_param} =~ "/" ]]; then
|
||||
cd ${relative_path}
|
||||
first_param=${first_param/$relative_path\//}
|
||||
fi
|
||||
@@ -174,7 +174,7 @@ run_concurrent() {
|
||||
|
||||
cd $dir_scripts
|
||||
local relative_path="${first_param%/*}"
|
||||
if [[ ! -z ${relative_path} ]]; then
|
||||
if [[ ! -z ${relative_path} ]] && [[ ${first_param} =~ "/" ]]; then
|
||||
cd ${relative_path}
|
||||
first_param=${first_param/$relative_path\//}
|
||||
fi
|
||||
@@ -248,7 +248,7 @@ run_designated() {
|
||||
|
||||
cd $dir_scripts
|
||||
local relative_path="${file_param%/*}"
|
||||
if [[ ! -z ${relative_path} ]]; then
|
||||
if [[ ! -z ${relative_path} ]] && [[ ${file_param} =~ "/" ]]; then
|
||||
cd ${relative_path}
|
||||
file_param=${file_param/$relative_path\//}
|
||||
fi
|
||||
|
||||
+13
-19
@@ -318,17 +318,11 @@ patch_version() {
|
||||
reload_pm2() {
|
||||
pm2 l &>/dev/null
|
||||
|
||||
if [[ $(pm2 info panel 2>/dev/null) ]]; then
|
||||
pm2 reload panel --source-map-support --time &>/dev/null
|
||||
else
|
||||
pm2 start $dir_root/build/app.js -n panel --source-map-support --time &>/dev/null
|
||||
fi
|
||||
pm2 delete panel --source-map-support --time &>/dev/null
|
||||
pm2 start $dir_root/build/app.js -n panel --source-map-support --time &>/dev/null
|
||||
|
||||
if [[ $(pm2 info schedule 2>/dev/null) ]]; then
|
||||
pm2 reload schedule --source-map-support --time &>/dev/null
|
||||
else
|
||||
pm2 start $dir_root/build/schedule.js -n schedule --source-map-support --time &>/dev/null
|
||||
fi
|
||||
pm2 delete schedule --source-map-support --time &>/dev/null
|
||||
pm2 start $dir_root/build/schedule.js -n schedule --source-map-support --time &>/dev/null
|
||||
}
|
||||
|
||||
## 对比脚本
|
||||
@@ -391,16 +385,11 @@ gen_list_repo() {
|
||||
if [[ $blackword ]]; then
|
||||
files=$(echo "$files" | egrep -v $blackword)
|
||||
fi
|
||||
if [[ $dependence ]]; then
|
||||
cd ${dir_scripts}
|
||||
depInScripts=$(eval $cmd | sed 's/^..//' | egrep -v $uniq_path | egrep $dependence)
|
||||
for dep in ${depInScripts}; do
|
||||
file_path=$(dirname $dep)
|
||||
[[ ! $file_path =~ "/" ]] && file_path=""
|
||||
make_dir "${dir_scripts}/${uniq_path}/${file_path#*/}"
|
||||
cp -f $dep "${dir_scripts}/${uniq_path}/${file_path#*/}"
|
||||
done
|
||||
|
||||
cp -f $file_notify_js "${dir_scripts}/${uniq_path}"
|
||||
cp -f $file_notify_py "${dir_scripts}/${uniq_path}"
|
||||
|
||||
if [[ $dependence ]]; then
|
||||
cd ${repo_path}
|
||||
results=$(eval $cmd | sed 's/^..//' | egrep $dependence)
|
||||
for _file in ${results}; do
|
||||
@@ -409,6 +398,11 @@ gen_list_repo() {
|
||||
cp -f $_file "${dir_scripts}/${uniq_path}/${file_path}"
|
||||
done
|
||||
fi
|
||||
|
||||
if [[ -d $dir_dep ]]; then
|
||||
cp $dir_dep/* "${dir_scripts}/${uniq_path}" &>/dev/null
|
||||
fi
|
||||
|
||||
for file in ${files}; do
|
||||
filename=$(basename $file)
|
||||
cp -f $file "$dir_scripts/${uniq_path}/${filename}"
|
||||
|
||||
Vendored
+17
-9
@@ -118,13 +118,17 @@ const Env = ({ headerStyle, isPhone, theme }: any) => {
|
||||
dataIndex: 'value',
|
||||
key: 'value',
|
||||
align: 'center' as const,
|
||||
width: '44%',
|
||||
width: '35%',
|
||||
ellipsis: {
|
||||
showTitle: false,
|
||||
},
|
||||
render: (text: string, record: any) => {
|
||||
return (
|
||||
<Tooltip placement="topLeft" title={text}>
|
||||
<Tooltip
|
||||
placement="topLeft"
|
||||
title={text}
|
||||
trigger={['hover', 'click']}
|
||||
>
|
||||
<span>{text}</span>
|
||||
</Tooltip>
|
||||
);
|
||||
@@ -141,19 +145,23 @@ const Env = ({ headerStyle, isPhone, theme }: any) => {
|
||||
dataIndex: 'timestamp',
|
||||
key: 'timestamp',
|
||||
align: 'center' as const,
|
||||
width: 164,
|
||||
width: 165,
|
||||
ellipsis: {
|
||||
showTitle: false,
|
||||
},
|
||||
render: (text: string, record: any) => {
|
||||
const language = navigator.language || navigator.languages[0];
|
||||
const date = new Date(text)
|
||||
.toLocaleString(language, {
|
||||
hour12: false,
|
||||
})
|
||||
.replace(' 24:', ' 00:')
|
||||
.toLocaleString(language, {
|
||||
hour12: false,
|
||||
})
|
||||
.replace(' 24:', ' 00:');
|
||||
return (
|
||||
<Tooltip placement="topLeft" title={date}>
|
||||
<Tooltip
|
||||
placement="topLeft"
|
||||
title={date}
|
||||
trigger={['hover', 'click']}
|
||||
>
|
||||
<span>{date}</span>
|
||||
</Tooltip>
|
||||
);
|
||||
@@ -528,7 +536,7 @@ const Env = ({ headerStyle, isPhone, theme }: any) => {
|
||||
dataSource={value}
|
||||
rowKey="_id"
|
||||
size="middle"
|
||||
scroll={{ x: 768, y: tableScrollHeight }}
|
||||
scroll={{ x: 1000, y: tableScrollHeight }}
|
||||
components={components}
|
||||
loading={loading}
|
||||
onRow={(record: any, index: number) => {
|
||||
|
||||
@@ -16,7 +16,6 @@ function getFilterData(keyword: string, data: any) {
|
||||
data.forEach((item: any) => {
|
||||
if (item.title.toLocaleLowerCase().includes(keyword)) {
|
||||
tree.push(item);
|
||||
expandedKeys.push(...item.children.map((x: any) => x.key));
|
||||
} else {
|
||||
const children: any[] = [];
|
||||
(item.children || []).forEach((subItem: any) => {
|
||||
@@ -29,7 +28,7 @@ function getFilterData(keyword: string, data: any) {
|
||||
...item,
|
||||
children,
|
||||
});
|
||||
expandedKeys.push(...children.map((x) => x.key));
|
||||
expandedKeys.push(item.key);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -47,6 +46,7 @@ const Log = ({ headerStyle, isPhone, theme }: any) => {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [height, setHeight] = useState<number>();
|
||||
const treeDom = useRef<any>();
|
||||
const [expandedKeys, setExpandedKeys] = useState<string[]>([]);
|
||||
|
||||
const getLogs = () => {
|
||||
setLoading(true);
|
||||
@@ -100,8 +100,12 @@ const Log = ({ headerStyle, isPhone, theme }: any) => {
|
||||
const onSearch = useCallback(
|
||||
(e) => {
|
||||
const keyword = e.target.value;
|
||||
const { tree } = getFilterData(keyword.toLocaleLowerCase(), data);
|
||||
const { tree, expandedKeys } = getFilterData(
|
||||
keyword.toLocaleLowerCase(),
|
||||
data,
|
||||
);
|
||||
setFilterData(tree);
|
||||
setExpandedKeys(expandedKeys);
|
||||
},
|
||||
[data, setFilterData],
|
||||
);
|
||||
@@ -127,7 +131,6 @@ const Log = ({ headerStyle, isPhone, theme }: any) => {
|
||||
treeData={data}
|
||||
placeholder="请选择日志文件"
|
||||
showSearch
|
||||
key="value"
|
||||
onSelect={onSelect}
|
||||
/>,
|
||||
]
|
||||
|
||||
@@ -1,28 +1,42 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Modal, message, Input, Form } from 'antd';
|
||||
import { Modal, message, Input, Form, Select } from 'antd';
|
||||
import { request } from '@/utils/http';
|
||||
import config from '@/utils/config';
|
||||
|
||||
const { Option } = Select;
|
||||
|
||||
const EditScriptNameModal = ({
|
||||
handleCancel,
|
||||
treeData,
|
||||
visible,
|
||||
}: {
|
||||
visible: boolean;
|
||||
handleCancel: (file?: { filename: string }) => void;
|
||||
treeData: any[];
|
||||
handleCancel: (file?: {
|
||||
filename: string;
|
||||
path: string;
|
||||
key: string;
|
||||
}) => void;
|
||||
}) => {
|
||||
const [form] = Form.useForm();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [dirs, setDirs] = useState<any[]>([]);
|
||||
|
||||
const handleOk = async (values: any) => {
|
||||
setLoading(true);
|
||||
values.path = values.path || '';
|
||||
request
|
||||
.post(`${config.apiPrefix}scripts`, {
|
||||
data: { filename: values.filename, content: '' },
|
||||
data: { filename: values.filename, path: values.path, content: '' },
|
||||
})
|
||||
.then(({ code, data }) => {
|
||||
if (code === 200) {
|
||||
message.success('保存文件成功');
|
||||
handleCancel({ filename: values.filename });
|
||||
handleCancel({
|
||||
filename: values.filename,
|
||||
path: values.path,
|
||||
key: `${values.path}-${values.filename}`,
|
||||
});
|
||||
} else {
|
||||
message.error(data);
|
||||
}
|
||||
@@ -33,6 +47,8 @@ const EditScriptNameModal = ({
|
||||
|
||||
useEffect(() => {
|
||||
form.resetFields();
|
||||
const originDirs = treeData.filter((x) => x.disabled);
|
||||
setDirs([{ key: '' }, ...originDirs]);
|
||||
}, [visible]);
|
||||
|
||||
return (
|
||||
@@ -56,10 +72,22 @@ const EditScriptNameModal = ({
|
||||
<Form form={form} layout="vertical" name="edit_name_modal">
|
||||
<Form.Item
|
||||
name="filename"
|
||||
label="文件名"
|
||||
rules={[{ required: true, message: '请输入文件名' }]}
|
||||
>
|
||||
<Input placeholder="请输入文件名" />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="父目录"
|
||||
name="path"
|
||||
initialValue={dirs && dirs.length > 0 ? dirs[0].key : ''}
|
||||
>
|
||||
<Select placeholder="请选择父目录">
|
||||
{dirs.map((x) => (
|
||||
<Option value={x.key}>{x.key || '根'}</Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Modal>
|
||||
);
|
||||
|
||||
+80
-18
@@ -35,16 +35,31 @@ import EditScriptNameModal from './editNameModal';
|
||||
const { Text } = Typography;
|
||||
|
||||
function getFilterData(keyword: string, data: any) {
|
||||
const expandedKeys: string[] = [];
|
||||
if (keyword) {
|
||||
const tree: any = [];
|
||||
data.forEach((item: any) => {
|
||||
if (item.title.toLocaleLowerCase().includes(keyword)) {
|
||||
tree.push(item);
|
||||
} else {
|
||||
const children: any[] = [];
|
||||
(item.children || []).forEach((subItem: any) => {
|
||||
if (subItem.title.toLocaleLowerCase().includes(keyword)) {
|
||||
children.push(subItem);
|
||||
}
|
||||
});
|
||||
if (children.length > 0) {
|
||||
tree.push({
|
||||
...item,
|
||||
children,
|
||||
});
|
||||
expandedKeys.push(item.key);
|
||||
}
|
||||
}
|
||||
});
|
||||
return { tree };
|
||||
return { tree, expandedKeys };
|
||||
}
|
||||
return { tree: data };
|
||||
return { tree: data, expandedKeys };
|
||||
}
|
||||
|
||||
const LangMap: any = {
|
||||
@@ -70,6 +85,7 @@ const Script = ({ headerStyle, isPhone, theme }: any) => {
|
||||
const editorRef = useRef<any>(null);
|
||||
const [isAddFileModalVisible, setIsAddFileModalVisible] = useState(false);
|
||||
const [currentNode, setCurrentNode] = useState<any>();
|
||||
const [expandedKeys, setExpandedKeys] = useState<string[]>([]);
|
||||
|
||||
const getScripts = () => {
|
||||
setLoading(true);
|
||||
@@ -97,7 +113,7 @@ const Script = ({ headerStyle, isPhone, theme }: any) => {
|
||||
setValue('加载中...');
|
||||
const newMode = value ? LangMap[value.slice(-3)] : '';
|
||||
setMode(isPhone && newMode === 'typescript' ? 'javascript' : newMode);
|
||||
setSelect(value);
|
||||
setSelect(node.key);
|
||||
setTitle(node.parent || node.value);
|
||||
setCurrentNode(node);
|
||||
getDetail(node);
|
||||
@@ -132,7 +148,11 @@ const Script = ({ headerStyle, isPhone, theme }: any) => {
|
||||
(e) => {
|
||||
const keyword = e.target.value;
|
||||
setSearchValue(keyword);
|
||||
const { tree } = getFilterData(keyword.toLocaleLowerCase(), data);
|
||||
const { tree, expandedKeys } = getFilterData(
|
||||
keyword.toLocaleLowerCase(),
|
||||
data,
|
||||
);
|
||||
setExpandedKeys(expandedKeys);
|
||||
setFilterData(tree);
|
||||
},
|
||||
[data, setFilterData],
|
||||
@@ -147,7 +167,7 @@ const Script = ({ headerStyle, isPhone, theme }: any) => {
|
||||
const cancelEdit = () => {
|
||||
setIsEditing(false);
|
||||
setValue('加载中...');
|
||||
getDetail({ value: select });
|
||||
getDetail(currentNode);
|
||||
};
|
||||
|
||||
const saveFile = () => {
|
||||
@@ -157,7 +177,7 @@ const Script = ({ headerStyle, isPhone, theme }: any) => {
|
||||
<>
|
||||
确认保存文件
|
||||
<Text style={{ wordBreak: 'break-all' }} type="warning">
|
||||
{select}
|
||||
{currentNode.value}
|
||||
</Text>{' '}
|
||||
,保存后不可恢复
|
||||
</>
|
||||
@@ -170,7 +190,7 @@ const Script = ({ headerStyle, isPhone, theme }: any) => {
|
||||
request
|
||||
.put(`${config.apiPrefix}scripts`, {
|
||||
data: {
|
||||
filename: select,
|
||||
filename: currentNode.value,
|
||||
path: currentNode.parent || '',
|
||||
content,
|
||||
},
|
||||
@@ -210,15 +230,30 @@ const Script = ({ headerStyle, isPhone, theme }: any) => {
|
||||
request
|
||||
.delete(`${config.apiPrefix}scripts`, {
|
||||
data: {
|
||||
filename: select,
|
||||
filename: currentNode.value,
|
||||
path: currentNode.parent || '',
|
||||
},
|
||||
})
|
||||
.then((_data: any) => {
|
||||
if (_data.code === 200) {
|
||||
message.success(`删除成功`);
|
||||
let newData = [...data];
|
||||
const index = newData.findIndex((x) => x.value === select);
|
||||
newData.splice(index, 1);
|
||||
if (currentNode.parent) {
|
||||
const parentNodeIndex = newData.findIndex(
|
||||
(x) => x.key === currentNode.parent,
|
||||
);
|
||||
const parentNode = newData[parentNodeIndex];
|
||||
const index = parentNode.children.findIndex(
|
||||
(y) => y.key === currentNode.key,
|
||||
);
|
||||
parentNode.children.splice(index, 1);
|
||||
newData.splice(parentNodeIndex, 1, { ...parentNode });
|
||||
} else {
|
||||
const index = newData.findIndex(
|
||||
(x) => x.key === currentNode.key,
|
||||
);
|
||||
newData.splice(index, 1);
|
||||
}
|
||||
setData(newData);
|
||||
} else {
|
||||
message.error(_data);
|
||||
@@ -236,12 +271,27 @@ const Script = ({ headerStyle, isPhone, theme }: any) => {
|
||||
};
|
||||
|
||||
const addFileModalClose = (
|
||||
{ filename }: { filename: string } = { filename: '' },
|
||||
{ filename, path, key }: { filename: string; path: string; key: string } = {
|
||||
filename: '',
|
||||
path: '',
|
||||
key: '',
|
||||
},
|
||||
) => {
|
||||
if (filename) {
|
||||
const newData = [...data];
|
||||
const _file = { title: filename, key: filename, value: filename };
|
||||
newData.unshift(_file);
|
||||
const _file = { title: filename, key, value: filename, parent: path };
|
||||
if (path) {
|
||||
const parentNodeIndex = newData.findIndex((x) => x.key === path);
|
||||
const parentNode = newData[parentNodeIndex];
|
||||
if (parentNode.children && parentNode.children.length > 0) {
|
||||
parentNode.children.unshift(_file);
|
||||
} else {
|
||||
parentNode.children = [_file];
|
||||
}
|
||||
newData.splice(parentNodeIndex, 1, { ...parentNode });
|
||||
} else {
|
||||
newData.unshift(_file);
|
||||
}
|
||||
setData(newData);
|
||||
onSelect(_file.value, _file);
|
||||
setIsEditing(true);
|
||||
@@ -253,7 +303,7 @@ const Script = ({ headerStyle, isPhone, theme }: any) => {
|
||||
request
|
||||
.post(`${config.apiPrefix}scripts/download`, {
|
||||
data: {
|
||||
filename: select,
|
||||
filename: currentNode.value,
|
||||
},
|
||||
})
|
||||
.then((_data: any) => {
|
||||
@@ -261,7 +311,7 @@ const Script = ({ headerStyle, isPhone, theme }: any) => {
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = select;
|
||||
a.download = currentNode.value;
|
||||
document.documentElement.appendChild(a);
|
||||
a.click();
|
||||
document.documentElement.removeChild(a);
|
||||
@@ -299,10 +349,20 @@ const Script = ({ headerStyle, isPhone, theme }: any) => {
|
||||
<Menu.Item key="add" icon={<PlusOutlined />} onClick={addFile}>
|
||||
添加
|
||||
</Menu.Item>
|
||||
<Menu.Item key="edit" icon={<EditOutlined />} onClick={editFile}>
|
||||
<Menu.Item
|
||||
key="edit"
|
||||
icon={<EditOutlined />}
|
||||
onClick={editFile}
|
||||
disabled={!select}
|
||||
>
|
||||
编辑
|
||||
</Menu.Item>
|
||||
<Menu.Item key="delete" icon={<DeleteOutlined />} onClick={deleteFile}>
|
||||
<Menu.Item
|
||||
key="delete"
|
||||
icon={<DeleteOutlined />}
|
||||
onClick={deleteFile}
|
||||
disabled={!select}
|
||||
>
|
||||
删除
|
||||
</Menu.Item>
|
||||
</Menu>
|
||||
@@ -323,7 +383,6 @@ const Script = ({ headerStyle, isPhone, theme }: any) => {
|
||||
treeData={data}
|
||||
placeholder="请选择脚本文件"
|
||||
showSearch
|
||||
key="value"
|
||||
onSelect={onSelect}
|
||||
/>,
|
||||
<Dropdown overlay={menu} trigger={['click']}>
|
||||
@@ -349,6 +408,7 @@ const Script = ({ headerStyle, isPhone, theme }: any) => {
|
||||
</Tooltip>,
|
||||
<Tooltip title="编辑">
|
||||
<Button
|
||||
disabled={!select}
|
||||
type="primary"
|
||||
onClick={editFile}
|
||||
icon={<EditOutlined />}
|
||||
@@ -357,6 +417,7 @@ const Script = ({ headerStyle, isPhone, theme }: any) => {
|
||||
<Tooltip title="删除">
|
||||
<Button
|
||||
type="primary"
|
||||
disabled={!select}
|
||||
onClick={deleteFile}
|
||||
icon={<DeleteOutlined />}
|
||||
/>
|
||||
@@ -440,6 +501,7 @@ const Script = ({ headerStyle, isPhone, theme }: any) => {
|
||||
/>
|
||||
<EditScriptNameModal
|
||||
visible={isAddFileModalVisible}
|
||||
treeData={data}
|
||||
handleCancel={addFileModalClose}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -64,6 +64,7 @@ export default {
|
||||
logs: '任务日志',
|
||||
},
|
||||
notificationModes: [
|
||||
{ value: 'gotify', label: 'Gotify' },
|
||||
{ value: 'goCqHttpBot', label: 'GoCqHttpBot' },
|
||||
{ value: 'serverChan', label: 'Server酱' },
|
||||
{ value: 'bark', label: 'Bark' },
|
||||
@@ -77,6 +78,15 @@ export default {
|
||||
{ value: 'closed', label: '已关闭' },
|
||||
],
|
||||
notificationModeMap: {
|
||||
gotify: [
|
||||
{
|
||||
label: 'gotifyUrl',
|
||||
tip: 'gotify的url地址,例如 https://push.example.de:8080',
|
||||
required: true,
|
||||
},
|
||||
{ label: 'gotifyToken', tip: 'gotify的消息应用token码', required: true },
|
||||
{ label: 'gotifyPriority', tip: '推送消息的优先级' },
|
||||
],
|
||||
goCqHttpBot: [
|
||||
{
|
||||
label: 'goCqHttpBotUrl',
|
||||
|
||||
+8
-8
@@ -1,9 +1,9 @@
|
||||
export const version = '2.10.6';
|
||||
export const changeLogLink = 'https://t.me/jiao_long/225';
|
||||
export const changeLog = `2.10.6 版本说明
|
||||
1. 增加各版本自动打包workflow,感谢 https://github.com/fzls PR
|
||||
2. 环境变量添加更新时间,感谢 https://github.com/miniers PR
|
||||
3. 修复脚本管理结构
|
||||
4. 修复定时匹配规则
|
||||
5. 修复检查更新
|
||||
export const version = '2.10.8';
|
||||
export const changeLogLink = 'https://t.me/jiao_long/227';
|
||||
export const changeLog = `2.10.8 版本说明
|
||||
1. 脚本管理新建文件增加选择父目录
|
||||
2. 修复脚本管理更新文件
|
||||
3. 增加gotify推送,感谢 https://github.com/kilo5hz PR
|
||||
4. 修复不能复制deps目录文件
|
||||
5. 修复可能的玩客云问题
|
||||
`;
|
||||
|
||||
Reference in New Issue
Block a user