确认删除
@@ -481,12 +673,23 @@
var enabledCheckbox = document.getElementById('clicd-fw-enabled');
var defaultActionSelect = document.getElementById('clicd-fw-default-action');
var statusText = document.getElementById('clicd-fw-status-text');
+ var saveSettingsButton = document.getElementById('clicd-fw-save-settings');
+ var saveHint = document.getElementById('clicd-fw-save-hint');
+ var addModal = document.getElementById('clicd-fw-add-modal');
+ var addClose = document.getElementById('clicd-fw-add-close');
+ var addCancel = document.getElementById('clicd-fw-add-cancel');
+ var addConfirm = document.getElementById('clicd-fw-add-confirm');
+ var ruleModalTitle = document.getElementById('clicd-fw-rule-modal-title');
var deleteModal = document.getElementById('clicd-fw-delete-modal');
var deleteText = document.getElementById('clicd-fw-delete-text');
var deleteCancel = document.getElementById('clicd-fw-delete-cancel');
var deleteConfirm = document.getElementById('clicd-fw-delete-confirm');
var pendingDeleteRule = null;
var currentRules = [];
+ var ruleModalMode = 'add';
+ var editingRuleIndex = null;
+ var settingsDirty = false;
+ var busy = false;
function showMessage(type, text) {
message.className = 'clicd-fw-message' + (type === 'error' ? ' error' : '');
@@ -507,8 +710,31 @@
return panel.getAttribute('data-service-id') || '';
}
- function setBusy(busy) {
- panel.querySelectorAll('button, input, select').forEach(function (el) { el.disabled = !!busy; });
+ function updateSaveState() {
+ if (saveSettingsButton) {
+ saveSettingsButton.disabled = busy || !settingsDirty;
+ }
+ if (saveHint) {
+ saveHint.textContent = settingsDirty ? '有未保存更改,点击保存设置后生效' : '当前设置已保存';
+ }
+ }
+
+ function setBusy(value) {
+ busy = !!value;
+ panel.querySelectorAll('button, input, select').forEach(function (el) { el.disabled = busy; });
+ updateSaveState();
+ }
+
+ function markDirty(text) {
+ settingsDirty = true;
+ updateStatusText();
+ updateSaveState();
+ showMessage('success', text || '设置已修改,点击保存设置后生效');
+ }
+
+ function clearDirty() {
+ settingsDirty = false;
+ updateSaveState();
}
function escapeHtml(value) {
@@ -532,7 +758,7 @@
rulesContainer.innerHTML = '
暂无防火墙规则
';
return;
}
- rulesContainer.innerHTML = currentRules.map(function (rule, idx) {
+ var rows = currentRules.map(function (rule, idx) {
var dir = (rule.direction || 'in').toLowerCase();
var proto = (rule.protocol || 'tcp').toLowerCase();
var action = (rule.action || 'ACCEPT').toUpperCase();
@@ -540,110 +766,98 @@
var port = escapeHtml(portDisplay(rule.port));
var srcIp = escapeHtml(sourceIpDisplay(rule.source_ip));
var desc = escapeHtml(rule.description || '');
- var ruleId = escapeHtml(rule.id || '');
var enabled = rule.enabled !== false;
var enabledChecked = enabled ? 'checked' : '';
var dirClass = dir === 'in' ? 'clicd-fw-direction-in' : 'clicd-fw-direction-out';
var actionClass = 'clicd-fw-action-' + action;
- return '
' +
- '' +
- '
' +
- '' + (desc || '无说明') + '' +
- '
' +
- '
' +
- '
' +
- '
' +
- '
' +
- '
' +
- '
' +
- '
' +
- '
' +
- '
' +
- '' +
- '' +
- '
' +
- '
' +
- '
';
+ return '
' +
+ ' | ' +
+ '' + networkLabel(network) + ' | ' +
+ '' + (dir === 'in' ? '入站' : '出站') + ' | ' +
+ '' + escapeHtml(proto.toUpperCase()) + ' | ' +
+ '' + port + ' | ' +
+ '' + srcIp + ' | ' +
+ '' + (action === 'ACCEPT' ? '放行' : '拒绝') + ' | ' +
+ '' + (desc || '-') + ' | ' +
+ '' +
+ '' +
+ '' +
+ ' | ' +
+ '
';
}).join('');
+ rulesContainer.innerHTML = '
' +
+ '' +
+ '| 状态 | ' +
+ '网络 | ' +
+ '方向 | ' +
+ '协议 | ' +
+ '端口 | ' +
+ '来源/目标 IP | ' +
+ '动作 | ' +
+ '描述 | ' +
+ '操作 | ' +
+ '
' + rows + '
';
+
rulesContainer.querySelectorAll('.clicd-fw-rule-enabled').forEach(function (toggle, idx) {
toggle.addEventListener('change', function () {
var rule = currentRules[idx];
if (!rule) return;
rule.enabled = toggle.checked;
- saveFirewall();
+ markDirty('规则开关已修改,点击保存设置后生效');
});
});
}
- function getEditField(item, name) {
- return item.querySelector('[data-field="' + name + '"]');
+ function setRuleField(id, value) {
+ var field = document.getElementById(id);
+ if (field) {
+ field.value = value == null ? '' : value;
+ }
}
- function updateRuleFromFields(item, idx) {
- currentRules[idx].network = getEditField(item, 'network') ? getEditField(item, 'network').value : 'ipv4';
- currentRules[idx].direction = getEditField(item, 'direction') ? getEditField(item, 'direction').value : 'in';
- currentRules[idx].protocol = getEditField(item, 'protocol') ? getEditField(item, 'protocol').value : 'tcp';
- currentRules[idx].port = getEditField(item, 'port') ? getEditField(item, 'port').value : '';
- currentRules[idx].source_ip = getEditField(item, 'source_ip') ? getEditField(item, 'source_ip').value : '';
- currentRules[idx].action = getEditField(item, 'action') ? getEditField(item, 'action').value : 'ACCEPT';
- currentRules[idx].description = getEditField(item, 'description') ? getEditField(item, 'description').value : '';
+ function getRuleField(id, fallback) {
+ var field = document.getElementById(id);
+ if (!field) return fallback || '';
+ return field.value;
}
- function getAddRulePayload() {
+ function getRuleFormPayload(baseRule) {
+ var base = baseRule || {};
return {
- id: '',
- network: document.getElementById('clicd-fw-add-network').value,
- direction: document.getElementById('clicd-fw-add-direction').value,
- protocol: document.getElementById('clicd-fw-add-protocol').value,
- port: document.getElementById('clicd-fw-add-port').value,
- source_ip: document.getElementById('clicd-fw-add-source-ip').value,
- action: document.getElementById('clicd-fw-add-action').value,
- description: document.getElementById('clicd-fw-add-desc').value,
- enabled: true
+ id: base.id || '',
+ network: getRuleField('clicd-fw-add-network', 'ipv4'),
+ direction: getRuleField('clicd-fw-add-direction', 'in'),
+ protocol: getRuleField('clicd-fw-add-protocol', 'tcp'),
+ port: getRuleField('clicd-fw-add-port', ''),
+ source_ip: getRuleField('clicd-fw-add-source-ip', ''),
+ action: getRuleField('clicd-fw-add-action', 'DROP'),
+ description: getRuleField('clicd-fw-add-desc', ''),
+ enabled: baseRule ? base.enabled !== false : true
};
}
function clearAddForm() {
- document.getElementById('clicd-fw-add-network').value = 'ipv4';
- document.getElementById('clicd-fw-add-port').value = '';
- document.getElementById('clicd-fw-add-source-ip').value = '';
- document.getElementById('clicd-fw-add-action').value = 'ACCEPT';
- document.getElementById('clicd-fw-add-desc').value = '';
+ setRuleField('clicd-fw-add-network', 'ipv4');
+ setRuleField('clicd-fw-add-direction', 'in');
+ setRuleField('clicd-fw-add-protocol', 'tcp');
+ setRuleField('clicd-fw-add-port', '');
+ setRuleField('clicd-fw-add-source-ip', '');
+ setRuleField('clicd-fw-add-action', 'DROP');
+ setRuleField('clicd-fw-add-desc', '');
+ }
+
+ function fillRuleForm(rule) {
+ var item = rule || {};
+ setRuleField('clicd-fw-add-network', item.network || 'ipv4');
+ setRuleField('clicd-fw-add-direction', item.direction || 'in');
+ setRuleField('clicd-fw-add-protocol', item.protocol || 'tcp');
+ setRuleField('clicd-fw-add-port', item.port || '');
+ setRuleField('clicd-fw-add-source-ip', item.source_ip || '');
+ setRuleField('clicd-fw-add-action', item.action || 'DROP');
+ setRuleField('clicd-fw-add-desc', item.description || '');
}
- // ── FIX: 发 JSON body,走 firewallajax 路由 ──
function saveFirewall() {
var enabled = enabledCheckbox.checked;
var defaultAction = defaultActionSelect.value;
@@ -661,22 +875,22 @@
};
});
+ var body = new URLSearchParams();
+ body.set('id', serviceId());
+ body.set('func', 'firewallUpdate');
+ body.set('enabled', enabled ? 'true' : 'false');
+ body.set('default_action', defaultAction);
+ body.set('rules', JSON.stringify(rules));
+
setBusy(true);
fetch(endpoint(), {
method: 'POST',
headers: {
- 'Content-Type': 'application/json; charset=UTF-8',
+ 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
'Authorization': 'JWT {$Think.get.jwt}'
},
credentials: 'same-origin',
- body: JSON.stringify({
- id: serviceId(),
- func: 'firewallajax',
- action: 'update',
- enabled: enabled,
- default_action: defaultAction,
- rules: rules
- })
+ body: body.toString()
})
.then(function (res) { return res.text(); })
.then(function (text) {
@@ -685,10 +899,17 @@
showDebug((data.data && data.data.debug) || data.debug || data);
if (data.status === 200 || data.status === 'success') {
showMessage('success', data.msg || '防火墙设置已更新');
+ if (data.data && Object.prototype.hasOwnProperty.call(data.data, 'enabled')) {
+ enabledCheckbox.checked = data.data.enabled === true || data.data.enabled === 'true' || data.data.enabled === 1;
+ }
+ if (data.data && data.data.default_action && defaultActionSelect) {
+ defaultActionSelect.value = data.data.default_action;
+ }
if (data.data && Array.isArray(data.data.rules)) {
currentRules = data.data.rules;
renderRules(currentRules);
}
+ clearDirty();
updateStatusText();
} else {
showMessage('error', data.msg || '更新失败');
@@ -703,21 +924,20 @@
});
}
- // ── FIX: 发 JSON body,走 firewallajax 路由 ──
function loadFirewall() {
+ var body = new URLSearchParams();
+ body.set('id', serviceId());
+ body.set('func', 'firewallList');
+
setBusy(true);
fetch(endpoint(), {
method: 'POST',
headers: {
- 'Content-Type': 'application/json; charset=UTF-8',
+ 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
'Authorization': 'JWT {$Think.get.jwt}'
},
credentials: 'same-origin',
- body: JSON.stringify({
- id: serviceId(),
- func: 'firewallajax',
- action: 'list'
- })
+ body: body.toString()
})
.then(function (res) { return res.text(); })
.then(function (text) {
@@ -732,6 +952,7 @@
}
currentRules = Array.isArray(data.data.rules) ? data.data.rules : [];
renderRules(currentRules);
+ clearDirty();
updateStatusText();
}
} else {
@@ -751,9 +972,9 @@
function updateStatusText() {
if (enabledCheckbox.checked) {
- statusText.textContent = '已启用 - 默认' + (defaultActionSelect.value === 'DROP' ? '拒绝' : '放行') + '未匹配流量';
+ statusText.textContent = '已启用,未匹配规则的流量将被' + (defaultActionSelect.value === 'DROP' ? '拒绝' : '放行');
} else {
- statusText.textContent = '已禁用 - 所有流量不受限制';
+ statusText.textContent = '未启用时不接管该容器流量';
}
}
@@ -774,27 +995,51 @@
}
}
+ function openRuleModal(mode, idx) {
+ ruleModalMode = mode === 'edit' ? 'edit' : 'add';
+ editingRuleIndex = ruleModalMode === 'edit' ? idx : null;
+ if (ruleModalTitle) {
+ ruleModalTitle.textContent = ruleModalMode === 'edit' ? '编辑规则' : '添加规则';
+ }
+ if (ruleModalMode === 'edit' && currentRules[idx]) {
+ fillRuleForm(currentRules[idx]);
+ } else {
+ clearAddForm();
+ }
+ if (addModal) {
+ addModal.style.display = 'flex';
+ }
+ var portInput = document.getElementById('clicd-fw-add-port');
+ if (portInput) {
+ setTimeout(function () { portInput.focus(); }, 0);
+ }
+ }
+
+ function closeAddModal() {
+ ruleModalMode = 'add';
+ editingRuleIndex = null;
+ if (addModal) {
+ addModal.style.display = 'none';
+ }
+ }
+
panel.addEventListener('click', function (event) {
var button = event.target.closest('[data-clicd-fw-action]');
if (!button) return;
var action = button.getAttribute('data-clicd-fw-action');
- if (action === 'add-rule') {
- currentRules.push(getAddRulePayload());
- renderRules(currentRules);
- clearAddForm();
- saveFirewall();
+ if (action === 'open-add-rule') {
+ openRuleModal('add');
return;
}
- var item = button.closest('.clicd-fw-rule');
+ var item = button.closest('[data-rule-index]');
if (!item) return;
var idx = parseInt(item.getAttribute('data-rule-index'), 10);
if (isNaN(idx) || !currentRules[idx]) return;
- if (action === 'update-rule') {
- updateRuleFromFields(item, idx);
- saveFirewall();
+ if (action === 'edit-rule') {
+ openRuleModal('edit', idx);
return;
}
@@ -807,14 +1052,46 @@
enabledCheckbox.addEventListener('change', function () {
updateStatusText();
- saveFirewall();
+ markDirty('防火墙开关已修改,点击保存设置后生效');
});
defaultActionSelect.addEventListener('change', function () {
updateStatusText();
- saveFirewall();
+ markDirty('默认动作已修改,点击保存设置后生效');
});
+ if (saveSettingsButton) {
+ saveSettingsButton.addEventListener('click', saveFirewall);
+ }
+
+ if (addClose) {
+ addClose.addEventListener('click', closeAddModal);
+ }
+ if (addCancel) {
+ addCancel.addEventListener('click', closeAddModal);
+ }
+ if (addModal) {
+ addModal.addEventListener('click', function (event) {
+ if (event.target === addModal) closeAddModal();
+ });
+ }
+ if (addConfirm) {
+ addConfirm.addEventListener('click', function () {
+ if (ruleModalMode === 'edit' && editingRuleIndex !== null && currentRules[editingRuleIndex]) {
+ currentRules[editingRuleIndex] = getRuleFormPayload(currentRules[editingRuleIndex]);
+ renderRules(currentRules);
+ closeAddModal();
+ markDirty('规则已更新,点击保存设置后生效');
+ return;
+ }
+ currentRules.push(getRuleFormPayload(null));
+ renderRules(currentRules);
+ closeAddModal();
+ clearAddForm();
+ markDirty('规则已添加,点击保存设置后生效');
+ });
+ }
+
if (deleteCancel) {
deleteCancel.addEventListener('click', closeDeleteModal);
}
@@ -831,11 +1108,12 @@
if (idx >= 0 && idx < currentRules.length) {
currentRules.splice(idx, 1);
renderRules(currentRules);
- saveFirewall();
+ markDirty('规则已删除,点击保存设置后生效');
}
});
}
+ updateSaveState();
loadFirewall();
})();
-
\ No newline at end of file
+