mirror of
https://github.com/whyour/qinglong.git
synced 2025-12-21 13:25:41 +08:00
Support absolute paths like /dev/null for log redirection
Co-authored-by: whyour <22700758+whyour@users.noreply.github.com>
This commit is contained in:
parent
71073b8670
commit
51bc0dd8b1
|
|
@ -477,19 +477,33 @@ export default class CronService {
|
||||||
);
|
);
|
||||||
|
|
||||||
let { id, command, log_path, log_name } = cron;
|
let { id, command, log_path, log_name } = cron;
|
||||||
// Sanitize log_name to prevent path traversal
|
|
||||||
const sanitizedLogName = log_name
|
// Check if log_name is an absolute path (e.g., /dev/null)
|
||||||
? log_name.replace(/[\/\\\.]/g, '_').replace(/^_+|_+$/g, '')
|
const isAbsolutePath = log_name && log_name.startsWith('/');
|
||||||
: '';
|
|
||||||
const uniqPath =
|
let uniqPath: string;
|
||||||
sanitizedLogName || (await getUniqPath(command, `${id}`));
|
let absolutePath: string;
|
||||||
const logTime = dayjs().format('YYYY-MM-DD-HH-mm-ss-SSS');
|
let logPath: string;
|
||||||
const logDirPath = path.resolve(config.logPath, `${uniqPath}`);
|
|
||||||
if (log_path?.split('/')?.every((x) => x !== uniqPath)) {
|
if (isAbsolutePath) {
|
||||||
await fs.mkdir(logDirPath, { recursive: true });
|
// Use absolute path directly for special files like /dev/null
|
||||||
|
uniqPath = log_name!;
|
||||||
|
absolutePath = log_name!;
|
||||||
|
logPath = log_name!;
|
||||||
|
} else {
|
||||||
|
// Sanitize log_name to prevent path traversal for relative paths
|
||||||
|
const sanitizedLogName = log_name
|
||||||
|
? log_name.replace(/[\/\\\.]/g, '_').replace(/^_+|_+$/g, '')
|
||||||
|
: '';
|
||||||
|
uniqPath = sanitizedLogName || (await getUniqPath(command, `${id}`));
|
||||||
|
const logTime = dayjs().format('YYYY-MM-DD-HH-mm-ss-SSS');
|
||||||
|
const logDirPath = path.resolve(config.logPath, `${uniqPath}`);
|
||||||
|
if (log_path?.split('/')?.every((x) => x !== uniqPath)) {
|
||||||
|
await fs.mkdir(logDirPath, { recursive: true });
|
||||||
|
}
|
||||||
|
logPath = `${uniqPath}/${logTime}.log`;
|
||||||
|
absolutePath = path.resolve(config.logPath, `${logPath}`);
|
||||||
}
|
}
|
||||||
const logPath = `${uniqPath}/${logTime}.log`;
|
|
||||||
const absolutePath = path.resolve(config.logPath, `${logPath}`);
|
|
||||||
const cp = spawn(
|
const cp = spawn(
|
||||||
`real_log_path=${logPath} no_delay=true ${this.makeCommand(
|
`real_log_path=${logPath} no_delay=true ${this.makeCommand(
|
||||||
cron,
|
cron,
|
||||||
|
|
|
||||||
|
|
@ -41,8 +41,21 @@ export const commonCronSchema = {
|
||||||
.optional()
|
.optional()
|
||||||
.allow('')
|
.allow('')
|
||||||
.allow(null)
|
.allow(null)
|
||||||
.pattern(/^[a-zA-Z0-9_-]+$/)
|
.custom((value, helpers) => {
|
||||||
.max(100)
|
if (!value) return value;
|
||||||
|
// Allow absolute paths like /dev/null
|
||||||
|
if (value.startsWith('/')) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
// For relative names, enforce strict pattern
|
||||||
|
if (!/^[a-zA-Z0-9_-]+$/.test(value)) {
|
||||||
|
return helpers.error('string.pattern.base');
|
||||||
|
}
|
||||||
|
if (value.length > 100) {
|
||||||
|
return helpers.error('string.max');
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
})
|
||||||
.messages({
|
.messages({
|
||||||
'string.pattern.base': '日志名称只能包含字母、数字、下划线和连字符',
|
'string.pattern.base': '日志名称只能包含字母、数字、下划线和连字符',
|
||||||
'string.max': '日志名称不能超过100个字符',
|
'string.max': '日志名称不能超过100个字符',
|
||||||
|
|
|
||||||
|
|
@ -524,7 +524,9 @@
|
||||||
"清除成功": "Clean successful",
|
"清除成功": "Clean successful",
|
||||||
"日志名称": "Log Name",
|
"日志名称": "Log Name",
|
||||||
"自定义日志文件夹名称,用于区分不同任务的日志,留空则自动生成": "Custom log folder name to distinguish logs from different tasks. Leave blank to auto-generate",
|
"自定义日志文件夹名称,用于区分不同任务的日志,留空则自动生成": "Custom log folder name to distinguish logs from different tasks. Leave blank to auto-generate",
|
||||||
|
"自定义日志文件夹名称,用于区分不同任务的日志,留空则自动生成。支持绝对路径如 /dev/null": "Custom log folder name to distinguish logs from different tasks. Leave blank to auto-generate. Supports absolute paths like /dev/null",
|
||||||
"请输入自定义日志文件夹名称": "Please enter a custom log folder name",
|
"请输入自定义日志文件夹名称": "Please enter a custom log folder name",
|
||||||
|
"请输入自定义日志文件夹名称或绝对路径": "Please enter a custom log folder name or absolute path",
|
||||||
"日志名称只能包含字母、数字、下划线和连字符": "Log name can only contain letters, numbers, underscores and hyphens",
|
"日志名称只能包含字母、数字、下划线和连字符": "Log name can only contain letters, numbers, underscores and hyphens",
|
||||||
"日志名称不能超过100个字符": "Log name cannot exceed 100 characters"
|
"日志名称不能超过100个字符": "Log name cannot exceed 100 characters"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -524,7 +524,9 @@
|
||||||
"清除成功": "清除成功",
|
"清除成功": "清除成功",
|
||||||
"日志名称": "日志名称",
|
"日志名称": "日志名称",
|
||||||
"自定义日志文件夹名称,用于区分不同任务的日志,留空则自动生成": "自定义日志文件夹名称,用于区分不同任务的日志,留空则自动生成",
|
"自定义日志文件夹名称,用于区分不同任务的日志,留空则自动生成": "自定义日志文件夹名称,用于区分不同任务的日志,留空则自动生成",
|
||||||
|
"自定义日志文件夹名称,用于区分不同任务的日志,留空则自动生成。支持绝对路径如 /dev/null": "自定义日志文件夹名称,用于区分不同任务的日志,留空则自动生成。支持绝对路径如 /dev/null",
|
||||||
"请输入自定义日志文件夹名称": "请输入自定义日志文件夹名称",
|
"请输入自定义日志文件夹名称": "请输入自定义日志文件夹名称",
|
||||||
|
"请输入自定义日志文件夹名称或绝对路径": "请输入自定义日志文件夹名称或绝对路径",
|
||||||
"日志名称只能包含字母、数字、下划线和连字符": "日志名称只能包含字母、数字、下划线和连字符",
|
"日志名称只能包含字母、数字、下划线和连字符": "日志名称只能包含字母、数字、下划线和连字符",
|
||||||
"日志名称不能超过100个字符": "日志名称不能超过100个字符"
|
"日志名称不能超过100个字符": "日志名称不能超过100个字符"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -184,22 +184,31 @@ const CronModal = ({
|
||||||
name="log_name"
|
name="log_name"
|
||||||
label={intl.get('日志名称')}
|
label={intl.get('日志名称')}
|
||||||
tooltip={intl.get(
|
tooltip={intl.get(
|
||||||
'自定义日志文件夹名称,用于区分不同任务的日志,留空则自动生成',
|
'自定义日志文件夹名称,用于区分不同任务的日志,留空则自动生成。支持绝对路径如 /dev/null',
|
||||||
)}
|
)}
|
||||||
rules={[
|
rules={[
|
||||||
{
|
{
|
||||||
pattern: /^[a-zA-Z0-9_-]*$/,
|
validator: (_, value) => {
|
||||||
message: intl.get('日志名称只能包含字母、数字、下划线和连字符'),
|
if (!value) return Promise.resolve();
|
||||||
},
|
// Allow absolute paths
|
||||||
{
|
if (value.startsWith('/')) return Promise.resolve();
|
||||||
max: 100,
|
// For relative names, enforce strict pattern
|
||||||
message: intl.get('日志名称不能超过100个字符'),
|
if (!/^[a-zA-Z0-9_-]+$/.test(value)) {
|
||||||
|
return Promise.reject(
|
||||||
|
intl.get('日志名称只能包含字母、数字、下划线和连字符'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (value.length > 100) {
|
||||||
|
return Promise.reject(intl.get('日志名称不能超过100个字符'));
|
||||||
|
}
|
||||||
|
return Promise.resolve();
|
||||||
|
},
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<Input
|
<Input
|
||||||
placeholder={intl.get('请输入自定义日志文件夹名称')}
|
placeholder={intl.get('请输入自定义日志文件夹名称或绝对路径')}
|
||||||
maxLength={100}
|
maxLength={200}
|
||||||
/>
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user