定时任务支持自定义日志文件或者 /dev/null (#2823)

* Initial plan

* Add log_name field to enable custom log folder naming

Co-authored-by: whyour <22700758+whyour@users.noreply.github.com>

* Add database migration for log_name column

Co-authored-by: whyour <22700758+whyour@users.noreply.github.com>

* Add security validation to prevent path traversal attacks

Co-authored-by: whyour <22700758+whyour@users.noreply.github.com>

* Apply prettier formatting to modified files

Co-authored-by: whyour <22700758+whyour@users.noreply.github.com>

* Support absolute paths like /dev/null for log redirection

Co-authored-by: whyour <22700758+whyour@users.noreply.github.com>

* Restrict absolute paths to log directory except /dev/null

Co-authored-by: whyour <22700758+whyour@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: whyour <22700758+whyour@users.noreply.github.com>
This commit is contained in:
Copilot
2025-11-09 19:32:40 +08:00
committed by GitHub
co-authored by whyour copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
parent 0e28e1b6c4
commit c369514741
7 changed files with 160 additions and 12 deletions
+36 -1
View File
@@ -180,6 +180,42 @@ const CronModal = ({
<Form.Item name="labels" label={intl.get('标签')}>
<EditableTagGroup />
</Form.Item>
<Form.Item
name="log_name"
label={intl.get('日志名称')}
tooltip={intl.get(
'自定义日志文件夹名称,用于区分不同任务的日志,留空则自动生成。支持 /dev/null 丢弃日志,其他绝对路径必须在日志目录内',
)}
rules={[
{
validator: (_, value) => {
if (!value) return Promise.resolve();
// Allow /dev/null specifically
if (value === '/dev/null') return Promise.resolve();
// Warn about other absolute paths (server will validate)
if (value.startsWith('/')) {
// We can't validate the exact path on frontend, but inform user
return Promise.resolve();
}
// For relative names, enforce strict pattern
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
placeholder={intl.get('请输入自定义日志文件夹名称或 /dev/null')}
maxLength={200}
/>
</Form.Item>
<Form.Item
name="task_before"
label={intl.get('执行前')}
@@ -312,4 +348,3 @@ const CronLabelModal = ({
};
export { CronLabelModal, CronModal as default };