This commit is contained in:
Copilot 2025-11-08 17:39:15 +00:00 committed by GitHub
commit a8446f13cf
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 60 additions and 6 deletions

View File

@ -21,6 +21,7 @@ export class Crontab {
extra_schedules?: Array<{ schedule: string }>;
task_before?: string;
task_after?: string;
log_name?: string;
constructor(options: Crontab) {
this.name = options.name;
@ -45,6 +46,7 @@ export class Crontab {
this.extra_schedules = options.extra_schedules;
this.task_before = options.task_before;
this.task_after = options.task_after;
this.log_name = options.log_name;
}
}
@ -84,4 +86,5 @@ export const CrontabModel = sequelize.define<CronInstance>('Crontab', {
extra_schedules: DataTypes.JSON,
task_before: DataTypes.STRING,
task_after: DataTypes.STRING,
log_name: DataTypes.STRING,
});

View File

@ -56,6 +56,11 @@ export default async () => {
try {
await sequelize.query('alter table Crontabs add column task_after TEXT');
} catch (error) {}
try {
await sequelize.query(
'alter table Crontabs add column log_name VARCHAR(255)',
);
} catch (error) {}
Logger.info('✌️ DB loaded');
} catch (error) {

View File

@ -476,8 +476,13 @@ export default class CronService {
`[panel][开始执行任务] 参数: ${JSON.stringify(params)}`,
);
let { id, command, log_path } = cron;
const uniqPath = await getUniqPath(command, `${id}`);
let { id, command, log_path, log_name } = cron;
// Sanitize log_name to prevent path traversal
const sanitizedLogName = log_name
? log_name.replace(/[\/\\\.]/g, '_').replace(/^_+|_+$/g, '')
: '';
const 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)) {

View File

@ -37,4 +37,14 @@ export const commonCronSchema = {
extra_schedules: Joi.array().optional().allow(null),
task_before: Joi.string().optional().allow('').allow(null),
task_after: Joi.string().optional().allow('').allow(null),
log_name: Joi.string()
.optional()
.allow('')
.allow(null)
.pattern(/^[a-zA-Z0-9_-]+$/)
.max(100)
.messages({
'string.pattern.base': '日志名称只能包含字母、数字、下划线和连字符',
'string.max': '日志名称不能超过100个字符',
}),
};

View File

@ -521,5 +521,10 @@
"远程仓库缓存": "Remote repository cache",
"SSH 文件缓存": "SSH file cache",
"清除依赖缓存": "Clean dependency cache",
"清除成功": "Clean successful"
"清除成功": "Clean successful",
"日志名称": "Log Name",
"自定义日志文件夹名称,用于区分不同任务的日志,留空则自动生成": "Custom log folder name to distinguish logs from different tasks. Leave blank to auto-generate",
"请输入自定义日志文件夹名称": "Please enter a custom log folder name",
"日志名称只能包含字母、数字、下划线和连字符": "Log name can only contain letters, numbers, underscores and hyphens",
"日志名称不能超过100个字符": "Log name cannot exceed 100 characters"
}

View File

@ -521,5 +521,10 @@
"远程仓库缓存": "远程仓库缓存",
"SSH 文件缓存": "SSH 文件缓存",
"清除依赖缓存": "清除依赖缓存",
"清除成功": "清除成功"
"清除成功": "清除成功",
"日志名称": "日志名称",
"自定义日志文件夹名称,用于区分不同任务的日志,留空则自动生成": "自定义日志文件夹名称,用于区分不同任务的日志,留空则自动生成",
"请输入自定义日志文件夹名称": "请输入自定义日志文件夹名称",
"日志名称只能包含字母、数字、下划线和连字符": "日志名称只能包含字母、数字、下划线和连字符",
"日志名称不能超过100个字符": "日志名称不能超过100个字符"
}

View File

@ -180,6 +180,28 @@ const CronModal = ({
<Form.Item name="labels" label={intl.get('标签')}>
<EditableTagGroup />
</Form.Item>
<Form.Item
name="log_name"
label={intl.get('日志名称')}
tooltip={intl.get(
'自定义日志文件夹名称,用于区分不同任务的日志,留空则自动生成',
)}
rules={[
{
pattern: /^[a-zA-Z0-9_-]*$/,
message: intl.get('日志名称只能包含字母、数字、下划线和连字符'),
},
{
max: 100,
message: intl.get('日志名称不能超过100个字符'),
},
]}
>
<Input
placeholder={intl.get('请输入自定义日志文件夹名称')}
maxLength={100}
/>
</Form.Item>
<Form.Item
name="task_before"
label={intl.get('执行前')}
@ -312,4 +334,3 @@ const CronLabelModal = ({
};
export { CronLabelModal, CronModal as default };