mirror of
https://github.com/Ed1s0nZ/CyberStrikeAI.git
synced 2026-04-21 10:16:32 +02:00
Add files via upload
This commit is contained in:
@@ -3621,7 +3621,7 @@ header {
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.form-group input,
|
||||
.form-group input:not([type="checkbox"]):not([type="radio"]),
|
||||
.form-group select {
|
||||
padding: 10px 12px;
|
||||
border: 1px solid var(--border-color);
|
||||
@@ -3644,30 +3644,43 @@ header {
|
||||
padding-right: 36px;
|
||||
}
|
||||
|
||||
.form-group input:focus,
|
||||
.form-group input:not([type="checkbox"]):not([type="radio"]):focus,
|
||||
.form-group select:focus {
|
||||
outline: none;
|
||||
border-color: var(--accent-color);
|
||||
box-shadow: 0 0 0 3px rgba(0, 102, 255, 0.1);
|
||||
}
|
||||
|
||||
.form-group input:hover,
|
||||
.form-group input:not([type="checkbox"]):not([type="radio"]):hover,
|
||||
.form-group select:hover {
|
||||
border-color: var(--accent-color);
|
||||
}
|
||||
|
||||
.form-group input.error,
|
||||
.form-group input:not([type="checkbox"]):not([type="radio"]).error,
|
||||
.form-group select.error {
|
||||
border-color: var(--error-color);
|
||||
box-shadow: 0 0 0 3px rgba(220, 53, 69, 0.1);
|
||||
}
|
||||
|
||||
.form-group input.error:focus,
|
||||
.form-group input:not([type="checkbox"]):not([type="radio"]).error:focus,
|
||||
.form-group select.error:focus {
|
||||
border-color: var(--error-color);
|
||||
box-shadow: 0 0 0 3px rgba(220, 53, 69, 0.2);
|
||||
}
|
||||
|
||||
.batch-execute-now-label {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
width: fit-content;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.batch-execute-now-label input[type="checkbox"] {
|
||||
width: auto;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* 现代化复选框样式 */
|
||||
.checkbox-label {
|
||||
display: flex !important;
|
||||
|
||||
@@ -1517,6 +1517,8 @@
|
||||
"cronExprPlaceholder": "e.g. 0 */2 * * * (run every 2 hours)",
|
||||
"cronExprHint": "Use standard 5-field Cron: minute hour day month weekday. Example: `0 2 * * *` runs at 02:00 daily.",
|
||||
"cronExprRequired": "Please fill in a Cron expression when Cron schedule is selected",
|
||||
"executeNow": "Run immediately after creation",
|
||||
"executeNowHint": "Default is off. When disabled, the queue stays pending and can be started manually later.",
|
||||
"tasksList": "Task list (one task per line)",
|
||||
"tasksListPlaceholder": "Enter task list, one per line",
|
||||
"tasksListPlaceholderExample": "Enter task list, one per line, for example:\nScan open ports of 192.168.1.1\nCheck if https://example.com has SQL injection\nEnumerate subdomains of example.com",
|
||||
@@ -1528,6 +1530,8 @@
|
||||
"title": "Batch queue details",
|
||||
"addTask": "Add task",
|
||||
"startExecute": "Start",
|
||||
"startExecuteNow": "Run now (one round)",
|
||||
"startExecuteNowConfirm": "This is a Cron queue. Clicking Start will run the current round immediately instead of waiting for the next Cron time. Continue?",
|
||||
"pauseQueue": "Pause queue",
|
||||
"deleteQueue": "Delete queue",
|
||||
"queueTitle": "Task title",
|
||||
|
||||
@@ -1517,6 +1517,8 @@
|
||||
"cronExprPlaceholder": "例如:0 */2 * * *(每2小时执行一次)",
|
||||
"cronExprHint": "采用标准 5 段 Cron:分 时 日 月 周,例如 `0 2 * * *` 表示每天 02:00 执行。",
|
||||
"cronExprRequired": "请选择 Cron 调度后填写 Cron 表达式",
|
||||
"executeNow": "创建后立即执行",
|
||||
"executeNowHint": "默认不立即执行;关闭后队列保持待执行,可在需要时手动开始。",
|
||||
"tasksList": "任务列表(每行一个任务)",
|
||||
"tasksListPlaceholder": "请输入任务列表,每行一个任务",
|
||||
"tasksListPlaceholderExample": "请输入任务列表,每行一个任务,例如:\n扫描 192.168.1.1 的开放端口\n检查 https://example.com 是否存在SQL注入\n枚举 example.com 的子域名",
|
||||
@@ -1528,6 +1530,8 @@
|
||||
"title": "批量任务队列详情",
|
||||
"addTask": "添加任务",
|
||||
"startExecute": "开始执行",
|
||||
"startExecuteNow": "立即执行一轮",
|
||||
"startExecuteNowConfirm": "这是 Cron 队列,点击后会立即执行当前这一轮,不会等待下次 Cron 时间。确定立即执行吗?",
|
||||
"pauseQueue": "暂停队列",
|
||||
"deleteQueue": "删除队列",
|
||||
"queueTitle": "任务标题",
|
||||
|
||||
+36
-4
@@ -57,6 +57,15 @@ function getBatchQueueStatusPresentation(queue) {
|
||||
return { ...base, ...empty };
|
||||
}
|
||||
|
||||
/** 队列是否处于「可改子任务列表/文案」的空闲态(与后端 batch_task_manager.queueAllowsTaskListMutationLocked 对齐) */
|
||||
function batchQueueAllowsSubtaskMutation(queue) {
|
||||
if (!queue) return false;
|
||||
if (queue.status === 'running') return false;
|
||||
const hasRunningSubtask = Array.isArray(queue.tasks) && queue.tasks.some(t => t && t.status === 'running');
|
||||
if (hasRunningSubtask) return false;
|
||||
return queue.status === 'pending' || queue.status === 'paused' || queue.status === 'completed' || queue.status === 'cancelled';
|
||||
}
|
||||
|
||||
// HTML转义函数(如果未定义)
|
||||
if (typeof escapeHtml === 'undefined') {
|
||||
function escapeHtml(text) {
|
||||
@@ -782,6 +791,7 @@ async function showBatchImportModal() {
|
||||
const agentModeSelect = document.getElementById('batch-queue-agent-mode');
|
||||
const scheduleModeSelect = document.getElementById('batch-queue-schedule-mode');
|
||||
const cronExprInput = document.getElementById('batch-queue-cron-expr');
|
||||
const executeNowCheckbox = document.getElementById('batch-queue-execute-now');
|
||||
if (modal && input) {
|
||||
input.value = '';
|
||||
if (titleInput) {
|
||||
@@ -800,6 +810,9 @@ async function showBatchImportModal() {
|
||||
if (cronExprInput) {
|
||||
cronExprInput.value = '';
|
||||
}
|
||||
if (executeNowCheckbox) {
|
||||
executeNowCheckbox.checked = false;
|
||||
}
|
||||
handleBatchScheduleModeChange();
|
||||
updateBatchImportStats('');
|
||||
|
||||
@@ -895,6 +908,7 @@ async function createBatchQueue() {
|
||||
const agentModeSelect = document.getElementById('batch-queue-agent-mode');
|
||||
const scheduleModeSelect = document.getElementById('batch-queue-schedule-mode');
|
||||
const cronExprInput = document.getElementById('batch-queue-cron-expr');
|
||||
const executeNowCheckbox = document.getElementById('batch-queue-execute-now');
|
||||
if (!input) return;
|
||||
|
||||
const text = input.value.trim();
|
||||
@@ -918,6 +932,7 @@ async function createBatchQueue() {
|
||||
const agentMode = agentModeSelect ? (agentModeSelect.value === 'multi' ? 'multi' : 'single') : 'single';
|
||||
const scheduleMode = scheduleModeSelect ? (scheduleModeSelect.value === 'cron' ? 'cron' : 'manual') : 'manual';
|
||||
const cronExpr = cronExprInput ? cronExprInput.value.trim() : '';
|
||||
const executeNow = executeNowCheckbox ? !!executeNowCheckbox.checked : false;
|
||||
if (scheduleMode === 'cron' && !cronExpr) {
|
||||
alert(_t('batchImportModal.cronExprRequired'));
|
||||
return;
|
||||
@@ -929,7 +944,7 @@ async function createBatchQueue() {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({ title, tasks, role, agentMode, scheduleMode, cronExpr }),
|
||||
body: JSON.stringify({ title, tasks, role, agentMode, scheduleMode, cronExpr, executeNow }),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
@@ -1266,6 +1281,7 @@ async function showBatchQueueDetail(queueId) {
|
||||
const queue = result.queue;
|
||||
batchQueuesState.currentQueueId = queueId;
|
||||
const pres = getBatchQueueStatusPresentation(queue);
|
||||
const allowSubtaskMutation = batchQueueAllowsSubtaskMutation(queue);
|
||||
|
||||
if (title) {
|
||||
// textContent 本身会做转义;这里不要再 escapeHtml,否则会把 && 显示成 &...(看起来像“变形/乱码”)
|
||||
@@ -1275,7 +1291,7 @@ async function showBatchQueueDetail(queueId) {
|
||||
// 更新按钮显示
|
||||
const pauseBtn = document.getElementById('batch-queue-pause-btn');
|
||||
if (addTaskBtn) {
|
||||
addTaskBtn.style.display = queue.status === 'pending' ? 'inline-block' : 'none';
|
||||
addTaskBtn.style.display = allowSubtaskMutation ? 'inline-block' : 'none';
|
||||
}
|
||||
if (startBtn) {
|
||||
// pending状态显示"开始执行",paused状态显示"继续执行"
|
||||
@@ -1283,7 +1299,10 @@ async function showBatchQueueDetail(queueId) {
|
||||
if (startBtn && queue.status === 'paused') {
|
||||
startBtn.textContent = _t('tasks.resumeExecute');
|
||||
} else if (startBtn && queue.status === 'pending') {
|
||||
startBtn.textContent = _t('batchQueueDetailModal.startExecute');
|
||||
const isCronPending = queue.scheduleMode === 'cron' && queue.scheduleEnabled !== false;
|
||||
startBtn.textContent = isCronPending
|
||||
? _t('batchQueueDetailModal.startExecuteNow')
|
||||
: _t('batchQueueDetailModal.startExecute');
|
||||
}
|
||||
}
|
||||
if (pauseBtn) {
|
||||
@@ -1374,7 +1393,7 @@ async function showBatchQueueDetail(queueId) {
|
||||
<h4>` + _t('batchQueueDetailModal.taskList') + `</h4>
|
||||
${queue.tasks.map((task, index) => {
|
||||
const taskStatus = taskStatusMap[task.status] || { text: task.status, class: 'batch-task-status-unknown' };
|
||||
const canEdit = queue.status === 'pending' && task.status === 'pending';
|
||||
const canEdit = allowSubtaskMutation && task.status !== 'running';
|
||||
const taskMessageEscaped = escapeHtml(task.message).replace(/'/g, "'").replace(/"/g, """).replace(/\n/g, "\\n");
|
||||
return `
|
||||
<div class="batch-task-item ${task.status === 'running' ? 'batch-task-item-active' : ''}" data-queue-id="${queue.id}" data-task-id="${task.id}" data-task-message="${taskMessageEscaped}">
|
||||
@@ -1423,6 +1442,19 @@ async function startBatchQueue() {
|
||||
if (!queueId) return;
|
||||
|
||||
try {
|
||||
// Cron 队列点击“开始执行”会立即运行一轮,这里二次确认避免误触
|
||||
const queueResponse = await apiFetch(`/api/batch-tasks/${queueId}`);
|
||||
if (!queueResponse.ok) {
|
||||
throw new Error(_t('tasks.getQueueDetailFailed'));
|
||||
}
|
||||
const queueResult = await queueResponse.json();
|
||||
const queue = queueResult && queueResult.queue ? queueResult.queue : null;
|
||||
const isCronPending = queue && queue.status === 'pending' && queue.scheduleMode === 'cron' && queue.scheduleEnabled !== false;
|
||||
if (isCronPending) {
|
||||
const okNow = confirm(_t('batchQueueDetailModal.startExecuteNowConfirm'));
|
||||
if (!okNow) return;
|
||||
}
|
||||
|
||||
const response = await apiFetch(`/api/batch-tasks/${queueId}/start`, {
|
||||
method: 'POST',
|
||||
});
|
||||
|
||||
@@ -2341,6 +2341,13 @@ version: 1.0.0<br>
|
||||
<input type="text" id="batch-queue-cron-expr" data-i18n="batchImportModal.cronExprPlaceholder" data-i18n-attr="placeholder" placeholder="例如:0 */2 * * *(每2小时执行一次)" />
|
||||
<div class="form-hint" style="margin-top: 4px;" data-i18n="batchImportModal.cronExprHint">采用标准 5 段 Cron:分 时 日 月 周,例如 `0 2 * * *` 表示每天 02:00 执行。</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="batch-queue-execute-now" class="batch-execute-now-label">
|
||||
<input type="checkbox" id="batch-queue-execute-now" />
|
||||
<span data-i18n="batchImportModal.executeNow">创建后立即执行</span>
|
||||
</label>
|
||||
<div class="form-hint" style="margin-top: 4px;" data-i18n="batchImportModal.executeNowHint">默认不立即执行;关闭后队列保持待执行,可在需要时手动开始。</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="batch-tasks-input"><span data-i18n="batchImportModal.tasksList">任务列表(每行一个任务)</span><span style="color: red;">*</span></label>
|
||||
<textarea id="batch-tasks-input" rows="15" data-i18n="batchImportModal.tasksListPlaceholderExample" data-i18n-attr="placeholder" placeholder="请输入任务列表,每行一个任务,例如: 扫描 192.168.1.1 的开放端口 检查 https://example.com 是否存在SQL注入 枚举 example.com 的子域名" style="font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace; font-size: 0.875rem; line-height: 1.5;"></textarea>
|
||||
|
||||
Reference in New Issue
Block a user