From 4a85def28dd43d778fcf03e58e1340d3268ee777 Mon Sep 17 00:00:00 2001 From: actions-user Date: Wed, 26 Nov 2025 00:13:14 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=8D=95=20Sync=202025-11-26=2000:13:14?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luci-app-bandix/Makefile | 2 +- .../resources/view/bandix/index.js | 1399 +++++++++++++---- luci-app-bandix/po/es/bandix.po | 90 ++ luci-app-bandix/po/fr/bandix.po | 92 +- luci-app-bandix/po/id/bandix.po | 90 ++ luci-app-bandix/po/ja/bandix.po | 90 ++ luci-app-bandix/po/pl/bandix.po | 90 ++ luci-app-bandix/po/ru/bandix.po | 92 +- luci-app-bandix/po/th/bandix.po | 90 ++ luci-app-bandix/po/zh_Hans/bandix.po | 92 +- luci-app-bandix/po/zh_Hant/bandix.po | 92 +- .../root/usr/libexec/rpcd/luci.bandix | 351 +++-- .../usr/share/rpcd/acl.d/luci-app-bandix.json | 12 +- openlist2/Makefile | 8 +- openwrt-bandix/Makefile | 4 +- 15 files changed, 2178 insertions(+), 416 deletions(-) diff --git a/luci-app-bandix/Makefile b/luci-app-bandix/Makefile index 6480c8a..1903e55 100644 --- a/luci-app-bandix/Makefile +++ b/luci-app-bandix/Makefile @@ -10,7 +10,7 @@ LUCI_DEPENDS:=+luci-base +luci-lib-jsonc +curl +bandix PKG_MAINTAINER:=timsaya -PKG_VERSION:=0.8.3 +PKG_VERSION:=0.9.0 PKG_RELEASE:=1 include $(TOPDIR)/feeds/luci/luci.mk diff --git a/luci-app-bandix/htdocs/luci-static/resources/view/bandix/index.js b/luci-app-bandix/htdocs/luci-static/resources/view/bandix/index.js index 584c30b..6051ebe 100644 --- a/luci-app-bandix/htdocs/luci-static/resources/view/bandix/index.js +++ b/luci-app-bandix/htdocs/luci-static/resources/view/bandix/index.js @@ -133,13 +133,6 @@ var callStatus = rpc.declare({ expect: {} }); -var callSetRateLimit = rpc.declare({ - object: 'luci.bandix', - method: 'setRateLimit', - params: ['mac', 'wide_tx_rate_limit', 'wide_rx_rate_limit'], - expect: { success: true } -}); - var callSetHostname = rpc.declare({ object: 'luci.bandix', method: 'setHostname', @@ -155,6 +148,27 @@ var callGetMetrics = rpc.declare({ expect: {} }); +// 定时限速 RPC +var callGetScheduleLimits = rpc.declare({ + object: 'luci.bandix', + method: 'getScheduleLimits', + expect: {} +}); + +var callSetScheduleLimit = rpc.declare({ + object: 'luci.bandix', + method: 'setScheduleLimit', + params: ['mac', 'start_time', 'end_time', 'days', 'wide_tx_rate_limit', 'wide_rx_rate_limit'], + expect: { success: true } +}); + +var callDeleteScheduleLimit = rpc.declare({ + object: 'luci.bandix', + method: 'deleteScheduleLimit', + params: ['mac', 'start_time', 'end_time', 'days'], + expect: { success: true } +}); + return view.extend({ load: function () { return Promise.all([ @@ -366,22 +380,28 @@ return view.extend({ .bandix-table th:nth-child(2), .bandix-table td:nth-child(2) { - width: 22%; + width: 20%; } .bandix-table th:nth-child(3), .bandix-table td:nth-child(3) { - width: 22%; + width: 20%; } .bandix-table th:nth-child(4), .bandix-table td:nth-child(4) { - width: 15%; + width: 25%; } .bandix-table th:nth-child(5), .bandix-table td:nth-child(5) { - width: 9%; + width: 10%; + } + + .schedule-rules-info { + display: flex; + flex-direction: column; + gap: 2px; } /* 类型联动的高亮与弱化 */ @@ -720,6 +740,206 @@ return view.extend({ outline: none; } + /* Tab 切换样式 */ + .modal-tabs { + display: flex; + border-bottom: 1px solid rgba(0, 0, 0, 0.1); + margin-bottom: 20px; + } + + @media (prefers-color-scheme: dark) { + .modal-tabs { + border-bottom-color: rgba(255, 255, 255, 0.15); + } + } + + .modal-tab { + flex: 1; + padding: 12px 16px; + text-align: center; + cursor: pointer; + border: none; + background: transparent; + font-size: 0.875rem; + font-weight: 500; + color: rgba(0, 0, 0, 0.6); + transition: all 0.2s ease; + border-bottom: 2px solid transparent; + } + + @media (prefers-color-scheme: dark) { + .modal-tab { + color: rgba(255, 255, 255, 0.6); + } + } + + .modal-tab:hover { + color: rgba(0, 0, 0, 0.8); + background-color: rgba(0, 0, 0, 0.02); + } + + @media (prefers-color-scheme: dark) { + .modal-tab:hover { + color: rgba(255, 255, 255, 0.8); + background-color: rgba(255, 255, 255, 0.05); + } + } + + .modal-tab.active { + color: #3b82f6; + border-bottom-color: #3b82f6; + font-weight: 600; + } + + .modal-tab-content { + display: none; + } + + .modal-tab-content.active { + display: block; + } + + .schedule-time-row { + display: flex; + gap: 12px; + align-items: center; + margin-bottom: 16px; + } + + .schedule-time-input { + flex: 1; + border-radius: 4px; + padding: 8px 12px; + font-size: 0.875rem; + transition: border-color 0.15s ease; + box-sizing: border-box; + } + + .schedule-days { + display: flex; + gap: 8px; + flex-wrap: wrap; + margin-bottom: 16px; + } + + .schedule-day-btn { + flex: 1; + min-width: 40px; + padding: 6px 8px; + border-radius: 4px; + border: 1px solid rgba(0, 0, 0, 0.2); + background: transparent; + cursor: pointer; + font-size: 0.75rem; + transition: all 0.15s ease; + text-align: center; + } + + @media (prefers-color-scheme: dark) { + .schedule-day-btn { + border-color: rgba(255, 255, 255, 0.2); + } + } + + .schedule-day-btn:hover { + background-color: rgba(0, 0, 0, 0.05); + } + + @media (prefers-color-scheme: dark) { + .schedule-day-btn:hover { + background-color: rgba(255, 255, 255, 0.05); + } + } + + .schedule-day-btn.active { + background-color: #3b82f6; + color: white; + border-color: #3b82f6; + } + + .schedule-rules-list { + min-height: 200px; + max-height: 400px; + overflow-y: auto; + border: 1px dashed rgba(0, 0, 0, 0.2); + border-radius: 4px; + padding: 16px; + } + + @media (prefers-color-scheme: dark) { + .schedule-rules-list { + border-color: rgba(255, 255, 255, 0.2); + } + } + + .schedule-rules-empty { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + min-height: 200px; + text-align: center; + color: rgba(0, 0, 0, 0.5); + font-size: 0.875rem; + } + + @media (prefers-color-scheme: dark) { + .schedule-rules-empty { + color: rgba(255, 255, 255, 0.5); + } + } + + .schedule-rule-item { + padding: 12px; + border: 1px solid rgba(0, 0, 0, 0.1); + border-radius: 4px; + margin-bottom: 8px; + display: flex; + justify-content: space-between; + align-items: center; + } + + @media (prefers-color-scheme: dark) { + .schedule-rule-item { + border-color: rgba(255, 255, 255, 0.15); + } + } + + .schedule-rule-info { + flex: 1; + } + + .schedule-rule-time { + font-weight: 600; + margin-bottom: 4px; + } + + .schedule-rule-days { + font-size: 0.75rem; + opacity: 0.7; + margin-bottom: 4px; + } + + .schedule-rule-limits { + font-size: 0.75rem; + opacity: 0.7; + } + + .schedule-rule-delete { + padding: 6px 12px; + font-size: 0.75rem; + cursor: pointer; + border-radius: 4px; + border: 1px solid rgba(239, 68, 68, 0.3); + background-color: rgba(239, 68, 68, 0.1); + color: #ef4444; + transition: all 0.15s ease; + } + + .schedule-rule-delete:hover { + background-color: rgba(239, 68, 68, 0.2); + } + .device-summary { border-radius: 4px; padding: 12px; @@ -757,6 +977,41 @@ return view.extend({ pointer-events: none; } + /* 确认对话框 */ + .confirm-dialog { + max-width: 400px; + width: 90%; + } + + .confirm-dialog .modal-body { + padding: 24px; + } + + .confirm-dialog-title { + font-size: 1.125rem; + font-weight: 600; + margin-bottom: 12px; + } + + .confirm-dialog-message { + font-size: 0.875rem; + line-height: 1.5; + color: rgba(0, 0, 0, 0.7); + margin-bottom: 20px; + } + + @media (prefers-color-scheme: dark) { + .confirm-dialog-message { + color: rgba(255, 255, 255, 0.7); + } + } + + .confirm-dialog-footer { + display: flex; + gap: 10px; + justify-content: flex-end; + } + /* 历史趋势 */ .history-header { display: flex; @@ -971,45 +1226,80 @@ return view.extend({ font-size: 0.875rem; } - .device-card-expandable { - display: none; + /* LAN流量样式(移动端直接显示) */ + .device-card-lan { margin-top: 12px; padding-top: 12px; border-top: 1px solid rgba(0, 0, 0, 0.1); } @media (prefers-color-scheme: dark) { - .device-card-expandable { + .device-card-lan { border-top-color: rgba(255, 255, 255, 0.15); } } - .device-card.expanded .device-card-expandable { - display: block; - } - - .device-card-toggle { - width: 100%; - margin-top: 8px; - padding: 6px; - font-size: 0.75rem; - text-align: center; - background: transparent; - border: 1px solid rgba(0, 0, 0, 0.1); - border-radius: 4px; - cursor: pointer; - opacity: 0.7; - transition: opacity 0.2s; + /* 规则显示样式 */ + .device-card-rules { + margin-top: 12px; + padding-top: 12px; + border-top: 1px solid rgba(0, 0, 0, 0.1); } @media (prefers-color-scheme: dark) { - .device-card-toggle { - border-color: rgba(255, 255, 255, 0.15); + .device-card-rules { + border-top-color: rgba(255, 255, 255, 0.15); } } - .device-card-toggle:hover { - opacity: 1; + .device-card-rules-content { + display: flex; + flex-direction: column; + gap: 6px; + padding: 8px 0; + } + + .device-card-rules-empty { + font-size: 0.75rem; + opacity: 0.6; + padding: 4px 0; + } + + .device-card-rules-count { + font-size: 0.8125rem; + font-weight: 600; + color: inherit; + margin-bottom: 2px; + } + + .device-card-rules-active-time { + font-size: 0.8125rem; + color: #10b981; + display: flex; + flex-direction: row; + align-items: center; + flex-wrap: wrap; + gap: 8px; + line-height: 1.4; + } + + .device-card-rules-limits { + font-size: 0.75rem; + opacity: 0.8; + margin-top: 2px; + word-break: break-word; + } + + .device-card-rules-more { + font-size: 0.7rem; + opacity: 0.6; + margin-top: 2px; + } + + .device-card-rules-inactive { + font-size: 0.8125rem; + opacity: 0.5; + margin-top: 4px; } } @@ -1183,42 +1473,402 @@ return view.extend({ // 创建限速设置模态框 var modal = E('div', { 'class': 'modal-overlay', 'id': 'rate-limit-modal' }, [ E('div', { 'class': 'modal' }, [ - E('div', { 'class': 'modal-header' }, [ - E('h3', { 'class': 'modal-title' }, _('Device Settings')) - ]), + // E('div', { 'class': 'modal-header' }, [ + // E('h3', { 'class': 'modal-title' }, _('Device Settings')) + // ]), E('div', { 'class': 'modal-body' }, [ E('div', { 'class': 'device-summary', 'id': 'modal-device-summary' }), E('div', { 'class': 'form-group' }, [ E('label', { 'class': 'form-label' }, _('Hostname')), - E('input', { 'type': 'text', 'class': 'form-input', 'id': 'device-hostname-input', 'placeholder': _('Please enter hostname') }), + E('div', { 'style': 'display: flex; gap: 8px; align-items: center;' }, [ + E('input', { 'type': 'text', 'class': 'form-input', 'id': 'device-hostname-input', 'placeholder': _('Please enter hostname'), 'style': 'flex: 1;' }), + E('button', { 'class': 'cbi-button cbi-button-positive', 'id': 'hostname-save-btn', 'style': 'flex-shrink: 0;' }, _('Save')) + ]), E('div', { 'style': 'font-size: 0.75rem; color: #6b7280; margin-top: 4px;' }, _('Set Hostname')) ]), - E('div', { 'class': 'form-group' }, [ - E('label', { 'class': 'form-label' }, _('Upload Limit')), - E('div', { 'style': 'display: flex; gap: 8px;' }, [ - E('input', { 'type': 'number', 'class': 'form-input', 'id': 'upload-limit-value', 'min': '0', 'step': '1', 'placeholder': '0' }), - E('select', { 'class': 'cbi-select', 'id': 'upload-limit-unit', 'style': 'width: 100px;' }) + // 定时限速 + E('div', { 'id': 'schedule-limit-tab' }, [ + // 描述和添加规则按钮 + E('div', { 'style': 'display: flex; align-items: center; justify-content: space-between; margin-bottom: 16px;' }, [ + E('span', { 'style': 'font-size: 0.875rem; opacity: 0.7;' }, _('Set rate limit rules for different time periods')), + E('button', { + 'type': 'button', + 'class': 'cbi-button cbi-button-action', + 'id': 'schedule-add-rule-btn', + 'style': 'display: flex; align-items: center; gap: 4px;' + }, [ + E('span', {}, '+'), + _('Add Rule') + ]) ]), - E('div', { 'style': 'font-size: 0.75rem; color: #6b7280; margin-top: 4px;' }, _('Tip: Enter 0 for unlimited')) - ]), - E('div', { 'class': 'form-group' }, [ - E('label', { 'class': 'form-label' }, _('Download Limit')), - E('div', { 'style': 'display: flex; gap: 8px;' }, [ - E('input', { 'type': 'number', 'class': 'form-input', 'id': 'download-limit-value', 'min': '0', 'step': '1', 'placeholder': '0' }), - E('select', { 'class': 'cbi-select', 'id': 'download-limit-unit', 'style': 'width: 100px;' }) - ]), - E('div', { 'style': 'font-size: 0.75rem; color: #6b7280; margin-top: 4px;' }, _('Tip: Enter 0 for unlimited')) + // 规则列表区域 + E('div', { 'class': 'schedule-rules-list', 'id': 'schedule-rules-list' }) ]) ]), E('div', { 'class': 'modal-footer' }, [ - E('button', { 'class': 'cbi-button cbi-button-reset', 'id': 'modal-cancel' }, _('Cancel')), - E('button', { 'class': 'cbi-button cbi-button-positive', 'id': 'modal-save' }, _('Save')) + E('button', { 'class': 'cbi-button cbi-button-reset', 'id': 'modal-close' }, _('Close')) ]) ]) ]); document.body.appendChild(modal); + // 创建添加规则模态框 + var addRuleModal = E('div', { 'class': 'modal-overlay', 'id': 'add-rule-modal' }, [ + E('div', { 'class': 'modal' }, [ + E('div', { 'class': 'modal-header' }, [ + E('h3', { 'class': 'modal-title' }, _('Add Schedule Rule')) + ]), + E('div', { 'class': 'modal-body' }, [ + E('div', { 'class': 'form-group' }, [ + E('label', { 'class': 'form-label' }, _('Time Slot')), + E('div', { 'class': 'schedule-time-row' }, [ + E('input', { 'type': 'time', 'class': 'schedule-time-input', 'id': 'add-rule-start-time' }), + E('span', {}, ' - '), + E('input', { 'type': 'time', 'class': 'schedule-time-input', 'id': 'add-rule-end-time' }) + ]) + ]), + E('div', { 'class': 'form-group' }, [ + E('label', { 'class': 'form-label' }, _('Days of Week')), + E('div', { 'class': 'schedule-days', 'id': 'add-rule-days' }, [ + E('button', { 'type': 'button', 'class': 'schedule-day-btn', 'data-day': '1' }, _('Mon')), + E('button', { 'type': 'button', 'class': 'schedule-day-btn', 'data-day': '2' }, _('Tue')), + E('button', { 'type': 'button', 'class': 'schedule-day-btn', 'data-day': '3' }, _('Wed')), + E('button', { 'type': 'button', 'class': 'schedule-day-btn', 'data-day': '4' }, _('Thu')), + E('button', { 'type': 'button', 'class': 'schedule-day-btn', 'data-day': '5' }, _('Fri')), + E('button', { 'type': 'button', 'class': 'schedule-day-btn', 'data-day': '6' }, _('Sat')), + E('button', { 'type': 'button', 'class': 'schedule-day-btn', 'data-day': '7' }, _('Sun')) + ]) + ]), + E('div', { 'class': 'form-group' }, [ + E('label', { 'class': 'form-label' }, _('Upload Limit')), + E('div', { 'style': 'display: flex; gap: 8px;' }, [ + E('input', { 'type': 'number', 'class': 'form-input', 'id': 'add-rule-upload-limit-value', 'min': '0', 'step': '1', 'placeholder': '0' }), + E('select', { 'class': 'cbi-select', 'id': 'add-rule-upload-limit-unit', 'style': 'width: 100px;' }) + ]), + E('div', { 'style': 'font-size: 0.75rem; color: #6b7280; margin-top: 4px;' }, _('Tip: Enter 0 for unlimited')) + ]), + E('div', { 'class': 'form-group', 'style': 'margin-bottom: 0;' }, [ + E('label', { 'class': 'form-label' }, _('Download Limit')), + E('div', { 'style': 'display: flex; gap: 8px;' }, [ + E('input', { 'type': 'number', 'class': 'form-input', 'id': 'add-rule-download-limit-value', 'min': '0', 'step': '1', 'placeholder': '0' }), + E('select', { 'class': 'cbi-select', 'id': 'add-rule-download-limit-unit', 'style': 'width: 100px;' }) + ]), + E('div', { 'style': 'font-size: 0.75rem; color: #6b7280; margin-top: 4px;' }, _('Tip: Enter 0 for unlimited')) + ]) + ]), + E('div', { 'class': 'modal-footer' }, [ + E('button', { 'class': 'cbi-button cbi-button-reset', 'id': 'add-rule-cancel' }, _('Cancel')), + E('button', { 'class': 'cbi-button cbi-button-positive', 'id': 'add-rule-save' }, _('Add')) + ]) + ]) + ]); + + document.body.appendChild(addRuleModal); + + // 创建确认对话框 + var confirmDialog = E('div', { 'class': 'modal-overlay', 'id': 'confirm-dialog-modal' }, [ + E('div', { 'class': 'modal confirm-dialog' }, [ + E('div', { 'class': 'modal-body' }, [ + E('div', { 'class': 'confirm-dialog-title', 'id': 'confirm-dialog-title' }, _('Confirm')), + E('div', { 'class': 'confirm-dialog-message', 'id': 'confirm-dialog-message' }, ''), + E('div', { 'class': 'confirm-dialog-footer' }, [ + E('button', { 'class': 'cbi-button cbi-button-reset', 'id': 'confirm-dialog-cancel' }, _('Cancel')), + E('button', { 'class': 'cbi-button cbi-button-negative', 'id': 'confirm-dialog-confirm' }, _('Confirm')) + ]) + ]) + ]) + ]); + + document.body.appendChild(confirmDialog); + + // 确认对话框相关变量 + var confirmDialogCallback = null; + + // 显示确认对话框 + function showConfirmDialog(title, message, onConfirm) { + document.getElementById('confirm-dialog-title').textContent = title || _('Confirm'); + document.getElementById('confirm-dialog-message').textContent = message || ''; + confirmDialogCallback = onConfirm; + + // 应用主题颜色 + try { + var cbiSection = document.querySelector('.cbi-section'); + var targetElement = cbiSection || document.querySelector('.main') || document.body; + var computedStyle = window.getComputedStyle(targetElement); + var bgColor = computedStyle.backgroundColor; + var textColor = computedStyle.color; + + var modalElement = confirmDialog.querySelector('.modal'); + + if (bgColor && bgColor !== 'rgba(0, 0, 0, 0)' && bgColor !== 'transparent') { + var rgbaMatch = bgColor.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*([\d.]+))?\)/); + if (rgbaMatch) { + var r = parseInt(rgbaMatch[1]); + var g = parseInt(rgbaMatch[2]); + var b = parseInt(rgbaMatch[3]); + var alpha = rgbaMatch[4] ? parseFloat(rgbaMatch[4]) : 1; + + if (alpha < 0.95) { + modalElement.style.backgroundColor = 'rgb(' + r + ', ' + g + ', ' + b + ')'; + } else { + modalElement.style.backgroundColor = bgColor; + } + } else { + modalElement.style.backgroundColor = bgColor; + } + } + + if (textColor && textColor !== 'rgba(0, 0, 0, 0)') { + modalElement.style.color = textColor; + } + } catch(e) {} + + confirmDialog.classList.add('show'); + } + + // 隐藏确认对话框 + function hideConfirmDialog() { + confirmDialog.classList.remove('show'); + confirmDialogCallback = null; + } + + // 确认对话框事件处理 + document.getElementById('confirm-dialog-confirm').addEventListener('click', function() { + if (confirmDialogCallback) { + confirmDialogCallback(); + } + hideConfirmDialog(); + }); + + document.getElementById('confirm-dialog-cancel').addEventListener('click', hideConfirmDialog); + + // 点击确认对话框背景关闭 + confirmDialog.addEventListener('click', function (e) { + if (e.target === this) { + hideConfirmDialog(); + } + }); + + // 日期选择按钮事件处理(添加规则模态框) + var addRuleDayButtons = addRuleModal.querySelectorAll('.schedule-day-btn'); + addRuleDayButtons.forEach(function(btn) { + btn.addEventListener('click', function() { + this.classList.toggle('active'); + }); + }); + + // 显示添加规则模态框 + function showAddRuleModal() { + if (!currentDevice) return; + + var addRuleModalEl = document.getElementById('add-rule-modal'); + var speedUnit = uci.get('bandix', 'traffic', 'speed_unit') || 'bytes'; + + // 动态填充单位选择器 + var uploadUnitSelect = document.getElementById('add-rule-upload-limit-unit'); + var downloadUnitSelect = document.getElementById('add-rule-download-limit-unit'); + + uploadUnitSelect.innerHTML = ''; + downloadUnitSelect.innerHTML = ''; + + if (speedUnit === 'bits') { + uploadUnitSelect.appendChild(E('option', { 'value': '125' }, 'Kbps')); + uploadUnitSelect.appendChild(E('option', { 'value': '125000' }, 'Mbps')); + uploadUnitSelect.appendChild(E('option', { 'value': '125000000' }, 'Gbps')); + + downloadUnitSelect.appendChild(E('option', { 'value': '125' }, 'Kbps')); + downloadUnitSelect.appendChild(E('option', { 'value': '125000' }, 'Mbps')); + downloadUnitSelect.appendChild(E('option', { 'value': '125000000' }, 'Gbps')); + } else { + uploadUnitSelect.appendChild(E('option', { 'value': '1024' }, 'KB/s')); + uploadUnitSelect.appendChild(E('option', { 'value': '1048576' }, 'MB/s')); + uploadUnitSelect.appendChild(E('option', { 'value': '1073741824' }, 'GB/s')); + + downloadUnitSelect.appendChild(E('option', { 'value': '1024' }, 'KB/s')); + downloadUnitSelect.appendChild(E('option', { 'value': '1048576' }, 'MB/s')); + downloadUnitSelect.appendChild(E('option', { 'value': '1073741824' }, 'GB/s')); + } + + // 重置表单 + resetAddRuleForm(); + + // 应用主题颜色 + try { + var cbiSection = document.querySelector('.cbi-section'); + var targetElement = cbiSection || document.querySelector('.main') || document.body; + var computedStyle = window.getComputedStyle(targetElement); + var bgColor = computedStyle.backgroundColor; + var textColor = computedStyle.color; + + var modalElement = addRuleModalEl.querySelector('.modal'); + + if (bgColor && bgColor !== 'rgba(0, 0, 0, 0)' && bgColor !== 'transparent') { + var rgbaMatch = bgColor.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*([\d.]+))?\)/); + if (rgbaMatch) { + var r = parseInt(rgbaMatch[1]); + var g = parseInt(rgbaMatch[2]); + var b = parseInt(rgbaMatch[3]); + var alpha = rgbaMatch[4] ? parseFloat(rgbaMatch[4]) : 1; + + if (alpha < 0.95) { + modalElement.style.backgroundColor = 'rgb(' + r + ', ' + g + ', ' + b + ')'; + } else { + modalElement.style.backgroundColor = bgColor; + } + } else { + modalElement.style.backgroundColor = bgColor; + } + } + + if (textColor && textColor !== 'rgba(0, 0, 0, 0)') { + modalElement.style.color = textColor; + } + } catch(e) {} + + // 显示模态框 + addRuleModalEl.classList.add('show'); + } + + // 隐藏添加规则模态框 + function hideAddRuleModal() { + var addRuleModalEl = document.getElementById('add-rule-modal'); + addRuleModalEl.classList.remove('show'); + } + + // 重置添加规则表单 + function resetAddRuleForm() { + document.getElementById('add-rule-start-time').value = '00:00'; + document.getElementById('add-rule-end-time').value = '23:59'; + // 默认选中所有7天 - 重新获取按钮引用 + var dayButtons = addRuleModal.querySelectorAll('.schedule-day-btn'); + dayButtons.forEach(function(btn) { + btn.classList.add('active'); + }); + var speedUnit = uci.get('bandix', 'traffic', 'speed_unit') || 'bytes'; + document.getElementById('add-rule-upload-limit-value').value = '0'; + document.getElementById('add-rule-download-limit-value').value = '0'; + document.getElementById('add-rule-upload-limit-unit').value = speedUnit === 'bits' ? '125' : '1024'; + document.getElementById('add-rule-download-limit-unit').value = speedUnit === 'bits' ? '125' : '1024'; + } + + // 添加规则按钮事件处理 + var scheduleAddRuleBtn = document.getElementById('schedule-add-rule-btn'); + if (scheduleAddRuleBtn) { + scheduleAddRuleBtn.addEventListener('click', function() { + showAddRuleModal(); + }); + } + + // 添加规则模态框取消按钮 + document.getElementById('add-rule-cancel').addEventListener('click', hideAddRuleModal); + + // 点击添加规则模态框背景关闭 + document.getElementById('add-rule-modal').addEventListener('click', function (e) { + if (e.target === this) { + hideAddRuleModal(); + } + }); + + // 保存定时限速规则(从添加规则模态框) + document.getElementById('add-rule-save').addEventListener('click', function() { + if (!currentDevice) { + console.error('No current device selected'); + return; + } + + var saveButton = this; + var originalText = saveButton.textContent; + + // 显示加载状态 + saveButton.innerHTML = '' + _('Adding...'); + saveButton.classList.add('btn-loading'); + saveButton.disabled = true; + + var startTime = document.getElementById('add-rule-start-time').value; + var endTime = document.getElementById('add-rule-end-time').value; + // HTML5 time 输入不支持 24:00,将 23:59 转换为 24:00 表示全天 + if (endTime === '23:59') { + endTime = '24:00'; + } + + // 重新获取日期按钮引用,确保获取最新状态 + var addRuleModalEl = document.getElementById('add-rule-modal'); + var dayButtons = addRuleModalEl.querySelectorAll('.schedule-day-btn'); + var selectedDays = []; + dayButtons.forEach(function(btn) { + if (btn.classList.contains('active')) { + selectedDays.push(parseInt(btn.getAttribute('data-day'))); + } + }); + + if (!startTime || !endTime) { + ui.addNotification(null, E('p', {}, _('Please set time slot')), 'error'); + saveButton.innerHTML = originalText; + saveButton.classList.remove('btn-loading'); + saveButton.disabled = false; + return; + } + + if (selectedDays.length === 0) { + ui.addNotification(null, E('p', {}, _('Please select at least one day')), 'error'); + saveButton.innerHTML = originalText; + saveButton.classList.remove('btn-loading'); + saveButton.disabled = false; + return; + } + + var speedUnit = uci.get('bandix', 'traffic', 'speed_unit') || 'bytes'; + var scheduleUploadValue = parseInt(document.getElementById('add-rule-upload-limit-value').value) || 0; + var scheduleUploadUnit = parseInt(document.getElementById('add-rule-upload-limit-unit').value); + var scheduleUploadLimit = scheduleUploadValue > 0 ? scheduleUploadValue * scheduleUploadUnit : 0; + + var scheduleDownloadValue = parseInt(document.getElementById('add-rule-download-limit-value').value) || 0; + var scheduleDownloadUnit = parseInt(document.getElementById('add-rule-download-limit-unit').value); + var scheduleDownloadLimit = scheduleDownloadValue > 0 ? scheduleDownloadValue * scheduleDownloadUnit : 0; + + console.log('Calling setScheduleLimit:', { + mac: currentDevice.mac, + startTime: startTime, + endTime: endTime, + days: selectedDays, + uploadLimit: scheduleUploadLimit, + downloadLimit: scheduleDownloadLimit + }); + + callSetScheduleLimit( + currentDevice.mac, + startTime, + endTime, + JSON.stringify(selectedDays), + scheduleUploadLimit, + scheduleDownloadLimit + ).then(function(result) { + console.log('setScheduleLimit result:', result); + // 恢复按钮状态 + saveButton.innerHTML = originalText; + saveButton.classList.remove('btn-loading'); + saveButton.disabled = false; + + // 隐藏模态框 + hideAddRuleModal(); + + // 重置表单 + resetAddRuleForm(); + + // 刷新规则列表 + loadScheduleRules(); + updateDeviceData(); + }).catch(function(error) { + console.error('Failed to add schedule rule:', error); + // 恢复按钮状态 + saveButton.innerHTML = originalText; + saveButton.classList.remove('btn-loading'); + saveButton.disabled = false; + ui.addNotification(null, E('p', {}, _('Failed to add schedule rule: ') + (error.message || error)), 'error'); + }); + }); + // 模态框事件处理 var currentDevice = null; var showRateLimitModal; @@ -1228,35 +1878,16 @@ return view.extend({ currentDevice = device; var modal = document.getElementById('rate-limit-modal'); var deviceSummary = document.getElementById('modal-device-summary'); - var speedUnit = uci.get('bandix', 'traffic', 'speed_unit') || 'bytes'; - - // 动态填充单位选择器 - var uploadUnitSelect = document.getElementById('upload-limit-unit'); - var downloadUnitSelect = document.getElementById('download-limit-unit'); - - // 清空现有选项 - uploadUnitSelect.innerHTML = ''; - downloadUnitSelect.innerHTML = ''; - - if (speedUnit === 'bits') { - // 比特单位选项 - 直接设置为对应的字节数 - uploadUnitSelect.appendChild(E('option', { 'value': '125' }, 'Kbps')); // 1000 bits/s / 8 = 125 bytes/s - uploadUnitSelect.appendChild(E('option', { 'value': '125000' }, 'Mbps')); // 1000000 bits/s / 8 = 125000 bytes/s - uploadUnitSelect.appendChild(E('option', { 'value': '125000000' }, 'Gbps')); // 1000000000 bits/s / 8 = 125000000 bytes/s - - downloadUnitSelect.appendChild(E('option', { 'value': '125' }, 'Kbps')); - downloadUnitSelect.appendChild(E('option', { 'value': '125000' }, 'Mbps')); - downloadUnitSelect.appendChild(E('option', { 'value': '125000000' }, 'Gbps')); - } else { - // 字节单位选项 - uploadUnitSelect.appendChild(E('option', { 'value': '1024' }, 'KB/s')); - uploadUnitSelect.appendChild(E('option', { 'value': '1048576' }, 'MB/s')); - uploadUnitSelect.appendChild(E('option', { 'value': '1073741824' }, 'GB/s')); - - downloadUnitSelect.appendChild(E('option', { 'value': '1024' }, 'KB/s')); - downloadUnitSelect.appendChild(E('option', { 'value': '1048576' }, 'MB/s')); - downloadUnitSelect.appendChild(E('option', { 'value': '1073741824' }, 'GB/s')); + // 清空定时限速规则列表并加载 + var rulesList = document.getElementById('schedule-rules-list'); + if (rulesList) { + rulesList.innerHTML = '
' + + _('No scheduled rules yet, click "Add Rule" to start setting') + + '
'; } + + // 加载定时限速规则列表 + loadScheduleRules(); // 更新设备信息 deviceSummary.innerHTML = E('div', {}, [ @@ -1267,84 +1898,6 @@ return view.extend({ // 设置当前hostname值 document.getElementById('device-hostname-input').value = device.hostname || ''; - // 设置当前限速值 - var uploadLimit = device.wide_tx_rate_limit || 0; - var downloadLimit = device.wide_rx_rate_limit || 0; - - // 设置上传限速值 - var uploadValue = uploadLimit; - var uploadUnit; - if (uploadValue === 0) { - document.getElementById('upload-limit-value').value = 0; - uploadUnit = speedUnit === 'bits' ? '125' : '1024'; - } else { - if (speedUnit === 'bits') { - // 转换为比特单位显示 - var uploadBits = uploadValue * 8; - if (uploadBits >= 1000000000) { - uploadValue = uploadBits / 1000000000; - uploadUnit = '125000000'; // Gbps对应的字节倍数 - } else if (uploadBits >= 1000000) { - uploadValue = uploadBits / 1000000; - uploadUnit = '125000'; // Mbps对应的字节倍数 - } else { - uploadValue = uploadBits / 1000; - uploadUnit = '125'; // Kbps对应的字节倍数 - } - } else { - // 字节单位显示 - if (uploadValue >= 1073741824) { - uploadValue = uploadValue / 1073741824; - uploadUnit = '1073741824'; - } else if (uploadValue >= 1048576) { - uploadValue = uploadValue / 1048576; - uploadUnit = '1048576'; - } else { - uploadValue = uploadValue / 1024; - uploadUnit = '1024'; - } - } - document.getElementById('upload-limit-value').value = Math.round(uploadValue); - } - document.getElementById('upload-limit-unit').value = uploadUnit; - - // 设置下载限速值 - var downloadValue = downloadLimit; - var downloadUnit; - if (downloadValue === 0) { - document.getElementById('download-limit-value').value = 0; - downloadUnit = speedUnit === 'bits' ? '125' : '1024'; - } else { - if (speedUnit === 'bits') { - // 转换为比特单位显示 - var downloadBits = downloadValue * 8; - if (downloadBits >= 1000000000) { - downloadValue = downloadBits / 1000000000; - downloadUnit = '125000000'; // Gbps对应的字节倍数 - } else if (downloadBits >= 1000000) { - downloadValue = downloadBits / 1000000; - downloadUnit = '125000'; // Mbps对应的字节倍数 - } else { - downloadValue = downloadBits / 1000; - downloadUnit = '125'; // Kbps对应的字节倍数 - } - } else { - // 字节单位显示 - if (downloadValue >= 1073741824) { - downloadValue = downloadValue / 1073741824; - downloadUnit = '1073741824'; - } else if (downloadValue >= 1048576) { - downloadValue = downloadValue / 1048576; - downloadUnit = '1048576'; - } else { - downloadValue = downloadValue / 1024; - downloadUnit = '1024'; - } - } - document.getElementById('download-limit-value').value = Math.round(downloadValue); - } - document.getElementById('download-limit-unit').value = downloadUnit; - // 应用 cbi-section 的颜色到模态框 try { // 优先从 cbi-section 获取颜色 @@ -1437,107 +1990,173 @@ return view.extend({ }, 300); } - // 保存限速设置 - function saveRateLimit() { + // 加载定时限速规则列表 + function loadScheduleRules() { + if (!currentDevice) return; + + var rulesList = document.getElementById('schedule-rules-list'); + if (!rulesList) return; + + rulesList.innerHTML = '
' + _('Loading...') + '
'; + + callGetScheduleLimits().then(function(res) { + // 检查响应格式 + if (!res) { + rulesList.innerHTML = '
' + _('No schedule rules') + '
'; + return; + } + + // 检查是否有错误 + if (res.success === false || res.error) { + var errorMsg = res.error || _('Failed to load schedule rules'); + rulesList.innerHTML = '
' + errorMsg + '
'; + return; + } + + // 检查数据格式 + var limits = []; + if (res.data && res.data.limits && Array.isArray(res.data.limits)) { + limits = res.data.limits; + } else if (Array.isArray(res.limits)) { + // 兼容不同的响应格式 + limits = res.limits; + } else if (Array.isArray(res)) { + // 如果直接返回数组 + limits = res; + } + + var speedUnit = uci.get('bandix', 'traffic', 'speed_unit') || 'bytes'; + + // 过滤出当前设备的规则 + var deviceRules = limits.filter(function(rule) { + return rule && rule.mac === currentDevice.mac; + }); + + // 清空列表 + rulesList.innerHTML = ''; + + if (deviceRules.length === 0) { + rulesList.innerHTML = '
' + + _('No scheduled rules yet, click "Add Rule" to start setting') + + '
'; + return; + } + + // 显示所有规则(支持多个规则) + deviceRules.forEach(function(rule) { + var daysText = ''; + // days 范围是 1-7 (Monday-Sunday) + var dayNames = { + 1: _('Mon'), + 2: _('Tue'), + 3: _('Wed'), + 4: _('Thu'), + 5: _('Fri'), + 6: _('Sat'), + 7: _('Sun') + }; + if (rule.time_slot && rule.time_slot.days && Array.isArray(rule.time_slot.days)) { + daysText = rule.time_slot.days.map(function(d) { return dayNames[d] || d; }).join(', '); + } + + var startTime = rule.time_slot && rule.time_slot.start ? rule.time_slot.start : ''; + var endTime = rule.time_slot && rule.time_slot.end ? rule.time_slot.end : ''; + var uploadLimit = rule.wide_tx_rate_limit || 0; + var downloadLimit = rule.wide_rx_rate_limit || 0; + + var ruleItem = E('div', { 'class': 'schedule-rule-item' }, [ + E('div', { 'class': 'schedule-rule-info' }, [ + E('div', { 'class': 'schedule-rule-time' }, startTime + ' - ' + endTime), + E('div', { 'class': 'schedule-rule-days' }, daysText), + E('div', { 'class': 'schedule-rule-limits' }, + '↑ ' + formatByterate(uploadLimit, speedUnit) + + ' / ↓ ' + formatByterate(downloadLimit, speedUnit) + ) + ]), + E('button', { + 'class': 'schedule-rule-delete', + 'title': _('Delete') + }, _('Delete')) + ]); + + ruleItem.querySelector('.schedule-rule-delete').addEventListener('click', function() { + showConfirmDialog( + _('Delete Schedule Rule'), + _('Are you sure you want to delete this schedule rule?'), + function() { + var days = rule.time_slot && rule.time_slot.days ? JSON.stringify(rule.time_slot.days) : '[]'; + callDeleteScheduleLimit( + rule.mac, + startTime, + endTime, + days + ).then(function() { + loadScheduleRules(); + updateDeviceData(); + }).catch(function(error) { + ui.addNotification(null, E('p', {}, _('Failed to delete schedule rule')), 'error'); + }); + } + ); + }); + + rulesList.appendChild(ruleItem); + }); + }).catch(function(error) { + console.error('Failed to load schedule rules:', error); + var errorMsg = _('Failed to load schedule rules'); + if (error && error.message) { + errorMsg += ': ' + error.message; + } + rulesList.innerHTML = '
' + errorMsg + '
'; + }); + } + + // 保存 hostname + function saveHostname() { if (!currentDevice) return; - var saveButton = document.getElementById('modal-save'); + var saveButton = document.getElementById('hostname-save-btn'); var originalText = saveButton.textContent; - // 显示加载状态 - saveButton.innerHTML = '' + _('Saving...'); - saveButton.classList.add('btn-loading'); - - var uploadLimit = 0; - var downloadLimit = 0; - var speedUnit = uci.get('bandix', 'traffic', 'speed_unit') || 'bytes'; - // 获取hostname值 var newHostname = document.getElementById('device-hostname-input').value.trim(); - // 获取上传限速值 - var uploadValue = parseInt(document.getElementById('upload-limit-value').value) || 0; - var uploadUnit = parseInt(document.getElementById('upload-limit-unit').value); - if (uploadValue > 0) { - // 选择器的值已经是正确的字节倍数,直接计算即可 - uploadLimit = uploadValue * uploadUnit; + // 如果hostname没有变化,不需要保存 + if (newHostname === (currentDevice.hostname || '')) { + return; } - // 获取下载限速值 - var downloadValue = parseInt(document.getElementById('download-limit-value').value) || 0; - var downloadUnit = parseInt(document.getElementById('download-limit-unit').value); - if (downloadValue > 0) { - // 选择器的值已经是正确的字节倍数,直接计算即可 - downloadLimit = downloadValue * downloadUnit; - } + // 显示加载状态 + saveButton.innerHTML = '' + _('Saving...'); + saveButton.classList.add('btn-loading'); + saveButton.disabled = true; - // console.log("mac", currentDevice.mac) - // console.log("uploadLimit", uploadLimit) - // console.log("downloadLimit", downloadLimit) - // console.log("newHostname", newHostname) - - // 创建Promise数组来并行处理hostname和限速设置 - var promises = []; - - // 如果hostname有变化,添加hostname设置Promise - if (newHostname !== (currentDevice.hostname || '')) { - promises.push( - callSetHostname(currentDevice.mac, newHostname).catch(function(error) { - return { hostnameError: error }; - }) - ); - } - - // 添加限速设置Promise - promises.push( - callSetRateLimit(currentDevice.mac, uploadLimit, downloadLimit).catch(function(error) { - return { rateLimitError: error }; - }) - ); - - // 并行执行所有设置 - Promise.all(promises).then(function (results) { + callSetHostname(currentDevice.mac, newHostname).then(function(result) { // 恢复按钮状态 saveButton.innerHTML = originalText; saveButton.classList.remove('btn-loading'); + saveButton.disabled = false; - var hasError = false; - var errorMessages = []; - - // 检查结果 - results.forEach(function(result, index) { - if (result && result.hostnameError) { - hasError = true; - errorMessages.push(_('Failed to set hostname')); - } else if (result && result.rateLimitError) { - hasError = true; - errorMessages.push(_('Failed to save settings')); - } else if (result !== true && result !== undefined) { - // 检查是否有其他错误 - if (result && result.error) { - hasError = true; - errorMessages.push(result.error); - } - } - }); - - if (hasError) { - ui.addNotification(null, E('p', {}, errorMessages.join(', ')), 'error'); - } else { - // 所有设置都成功 - hideRateLimitModal(); - } - }).catch(function (error) { + // 更新当前设备信息 + currentDevice.hostname = newHostname; + + // 刷新设备数据 + updateDeviceData(); + }).catch(function(error) { // 恢复按钮状态 saveButton.innerHTML = originalText; saveButton.classList.remove('btn-loading'); - ui.addNotification(null, E('p', {}, _('Failed to save settings')), 'error'); + saveButton.disabled = false; + ui.addNotification(null, E('p', {}, _('Failed to set hostname')), 'error'); }); } - // 绑定模态框事件 - document.getElementById('modal-cancel').addEventListener('click', hideRateLimitModal); - document.getElementById('modal-save').addEventListener('click', saveRateLimit); + // 绑定 hostname 保存按钮事件 + document.getElementById('hostname-save-btn').addEventListener('click', saveHostname); + + // 绑定关闭按钮事件 + document.getElementById('modal-close').addEventListener('click', hideRateLimitModal); // 点击模态框背景关闭 document.getElementById('rate-limit-modal').addEventListener('click', function (e) { @@ -1551,6 +2170,92 @@ return view.extend({ var lastHistoryData = null; // 最近一次拉取的原始 metrics 数据 var isHistoryLoading = false; // 防止轮询重入 + // 定时限速规则:全局存储 + var allScheduleRules = []; // 存储所有设备的定时限速规则 + var isScheduleRulesLoading = false; // 防止轮询重入 + + // 获取所有定时限速规则 + function fetchAllScheduleRules() { + if (isScheduleRulesLoading) return Promise.resolve(); + isScheduleRulesLoading = true; + + return callGetScheduleLimits().then(function(res) { + isScheduleRulesLoading = false; + + if (!res) { + allScheduleRules = []; + return; + } + + // 检查是否有错误 + if (res.success === false || res.error) { + allScheduleRules = []; + return; + } + + // 检查数据格式 + var limits = []; + if (res.data && res.data.limits && Array.isArray(res.data.limits)) { + limits = res.data.limits; + } else if (Array.isArray(res.limits)) { + limits = res.limits; + } else if (Array.isArray(res)) { + limits = res; + } + + allScheduleRules = limits || []; + }).catch(function(error) { + isScheduleRulesLoading = false; + console.error('Failed to fetch schedule rules:', error); + allScheduleRules = []; + }); + } + + // 判断规则是否在当前时间生效 + function isRuleActive(rule) { + if (!rule || !rule.time_slot) return false; + + var now = new Date(); + var currentDay = now.getDay(); // 0=Sunday, 1=Monday, ..., 6=Saturday + // 转换为 1-7 (Monday-Sunday) + var dayOfWeek = currentDay === 0 ? 7 : currentDay; + + // 检查是否在规则指定的日期中 + var days = rule.time_slot.days || []; + if (!Array.isArray(days) || days.length === 0) return false; + if (days.indexOf(dayOfWeek) === -1) return false; + + // 获取当前时间(HH:MM格式) + var currentTime = ('0' + now.getHours()).slice(-2) + ':' + ('0' + now.getMinutes()).slice(-2); + var startTime = rule.time_slot.start || ''; + var endTime = rule.time_slot.end || ''; + + if (!startTime || !endTime) return false; + + // 处理 24:00 的情况 + if (endTime === '24:00') { + endTime = '23:59'; + } + + // 比较时间 + if (startTime <= endTime) { + // 正常情况:开始时间 <= 结束时间 + return currentTime >= startTime && currentTime <= endTime; + } else { + // 跨天情况:开始时间 > 结束时间(例如 22:00 - 06:00) + return currentTime >= startTime || currentTime <= endTime; + } + } + + // 获取设备当前生效的规则 + function getActiveRulesForDevice(mac) { + if (!allScheduleRules || allScheduleRules.length === 0) return []; + + return allScheduleRules.filter(function(rule) { + return rule && rule.mac === mac && isRuleActive(rule); + }); + } + // 排序状态管理 var currentSortBy = localStorage.getItem('bandix_sort_by') || 'online'; // 默认按在线状态排序 var currentSortOrder = localStorage.getItem('bandix_sort_order') === 'true'; // false = 降序, true = 升序 @@ -2732,7 +3437,11 @@ function downsampleForMobile(data, labels, upSeries, downSeries, maxPoints) { // 定义更新设备数据的函数 function updateDeviceData() { - return callStatus().then(function (result) { + return Promise.all([ + callStatus(), + fetchAllScheduleRules() + ]).then(function (results) { + var result = results[0]; var trafficDiv = document.getElementById('traffic-status'); var deviceCountDiv = document.getElementById('device-count'); var statsGrid = document.getElementById('stats-grid'); @@ -2936,7 +3645,7 @@ function downsampleForMobile(data, labels, upSeries, downSeries, maxPoints) { createSortableHeader(_('Device Info'), 'online'), createSplitHeader(_('LAN Traffic'), 'lan_speed', 'lan_traffic'), createSplitHeader(_('WAN Traffic'), 'wan_speed', 'wan_traffic'), - E('th', {}, _('Rate Limit')), + E('th', {}, _('Schedule Rules')), E('th', {}, _('Actions')) ]) ]), @@ -3062,19 +3771,58 @@ function downsampleForMobile(data, labels, upSeries, downSeries, maxPoints) { ]) ]), - // 限速设置 - E('td', {}, [ - E('div', { 'class': 'limit-info' }, [ - E('div', { 'class': 'traffic-row' }, [ - E('span', { 'class': 'traffic-icon upload', 'style': 'font-size: 0.75rem;' }, '↑'), - E('span', { 'style': 'font-size: 0.875rem;' }, formatByterate(device.wide_tx_rate_limit || 0, speedUnit)) - ]), - E('div', { 'class': 'traffic-row' }, [ - E('span', { 'class': 'traffic-icon download', 'style': 'font-size: 0.75rem;' }, '↓'), - E('span', { 'style': 'font-size: 0.875rem;' }, formatByterate(device.wide_rx_rate_limit || 0, speedUnit)) - ]), - ]) - ]), + // 定时限速规则 + (function() { + var activeRules = getActiveRulesForDevice(device.mac); + var allDeviceRules = allScheduleRules.filter(function(r) { return r && r.mac === device.mac; }); + + var rulesInfo = E('div', { 'class': 'schedule-rules-info' }, []); + + if (allDeviceRules.length === 0) { + rulesInfo.appendChild(E('div', { 'style': 'font-size: 0.75rem; opacity: 0.6;' }, '-')); + } else { + // 显示规则数量 + rulesInfo.appendChild(E('div', { + 'style': 'font-size: 0.75rem; font-weight: 600; margin-bottom: 4px;' + }, allDeviceRules.length + ' ' + (allDeviceRules.length === 1 ? _('rule') : _('rules')))); + + // 显示当前生效的规则 + if (activeRules.length > 0) { + var activeRule = activeRules[0]; // 显示第一个生效的规则 + var startTime = activeRule.time_slot && activeRule.time_slot.start ? activeRule.time_slot.start : ''; + var endTime = activeRule.time_slot && activeRule.time_slot.end ? activeRule.time_slot.end : ''; + + var uploadLimit = activeRule.wide_tx_rate_limit || 0; + var downloadLimit = activeRule.wide_rx_rate_limit || 0; + + // 时间段和限速值放在同一行 + var timeSlotText = startTime + '-' + endTime; + var limitsText = []; + // 即使限速是0也显示 + limitsText.push('↑' + (uploadLimit > 0 ? formatByterate(uploadLimit, speedUnit) : _('Unlimited'))); + limitsText.push('↓' + (downloadLimit > 0 ? formatByterate(downloadLimit, speedUnit) : _('Unlimited'))); + + rulesInfo.appendChild(E('div', { + 'style': 'font-size: 0.75rem; color: #10b981; display: flex; align-items: center; gap: 8px; flex-wrap: wrap;' + }, [ + E('span', {}, '● ' + timeSlotText), + E('span', { 'style': 'opacity: 0.8; font-size: 0.7rem;' }, limitsText.join(' ')) + ])); + + if (activeRules.length > 1) { + rulesInfo.appendChild(E('div', { + 'style': 'font-size: 0.7rem; opacity: 0.6; margin-top: 2px;' + }, '+' + (activeRules.length - 1) + ' ' + _('more'))); + } + } else { + rulesInfo.appendChild(E('div', { + 'style': 'font-size: 0.75rem; opacity: 0.5;' + }, _('No active rule'))); + } + } + + return E('td', {}, rulesInfo); + })(), // 操作 E('td', {}, [ @@ -3108,7 +3856,7 @@ function downsampleForMobile(data, labels, upSeries, downSeries, maxPoints) { })() ]) ]), - // 卡片主要内容(WAN流量和限速) + // 卡片主要内容(WAN流量) E('div', { 'class': 'device-card-content' }, [ // WAN流量 E('div', { 'class': 'device-card-section' }, [ @@ -3125,64 +3873,86 @@ function downsampleForMobile(data, labels, upSeries, downSeries, maxPoints) { E('span', { 'style': 'font-size: 0.75rem; opacity: 0.7;' }, '(' + formatSize(device.wide_rx_bytes || 0) + ')') ]) ]) - ]), - // 限速设置 - E('div', { 'class': 'device-card-section' }, [ - E('div', { 'class': 'device-card-section-label' }, _('Rate Limit')), - E('div', { 'class': 'device-card-traffic' }, [ - E('div', { 'class': 'device-card-traffic-row' }, [ - E('span', { 'style': 'color: #f97316; font-size: 0.75rem; font-weight: bold;' }, '↑'), - E('span', { 'style': 'font-weight: 600;' }, formatByterate(device.wide_tx_rate_limit || 0, speedUnit)) - ]), - E('div', { 'class': 'device-card-traffic-row' }, [ - E('span', { 'style': 'color: #06b6d4; font-size: 0.75rem; font-weight: bold;' }, '↓'), - E('span', { 'style': 'font-weight: 600;' }, formatByterate(device.wide_rx_rate_limit || 0, speedUnit)) - ]) - ]) ]) ]), - // 可展开的详情(LAN流量) - E('div', { 'class': 'device-card-expandable' }, [ - E('div', { 'class': 'device-card-section', 'style': 'margin-top: 0;' }, [ - E('div', { 'class': 'device-card-section-label' }, _('LAN Traffic')), - E('div', { 'class': 'device-card-traffic' }, [ - E('div', { 'class': 'device-card-traffic-row' }, [ - E('span', { 'style': 'color: #f97316; font-size: 0.75rem; font-weight: bold;' }, '↑'), - E('span', { 'style': 'font-weight: 600;' }, formatByterate(device.local_tx_rate || 0, speedUnit)), - E('span', { 'style': 'font-size: 0.75rem; opacity: 0.7;' }, '(' + formatSize(device.local_tx_bytes || 0) + ')') - ]), - E('div', { 'class': 'device-card-traffic-row' }, [ - E('span', { 'style': 'color: #06b6d4; font-size: 0.75rem; font-weight: bold;' }, '↓'), - E('span', { 'style': 'font-weight: 600;' }, formatByterate(device.local_rx_rate || 0, speedUnit)), - E('span', { 'style': 'font-size: 0.75rem; opacity: 0.7;' }, '(' + formatSize(device.local_rx_bytes || 0) + ')') - ]) + // 定时限速规则 + (function() { + var activeRules = getActiveRulesForDevice(device.mac); + var allDeviceRules = allScheduleRules.filter(function(r) { return r && r.mac === device.mac; }); + + if (allDeviceRules.length === 0) { + return E('div', { 'class': 'device-card-section device-card-rules' }, [ + E('div', { 'class': 'device-card-section-label' }, _('Schedule Rules')), + E('div', { 'class': 'device-card-rules-empty' }, '-') + ]); + } + + var rulesContent = E('div', { 'class': 'device-card-rules-content' }); + + // 规则数量 + rulesContent.appendChild(E('div', { + 'class': 'device-card-rules-count' + }, allDeviceRules.length + ' ' + (allDeviceRules.length === 1 ? _('rule') : _('rules')))); + + if (activeRules.length > 0) { + var activeRule = activeRules[0]; + var startTime = activeRule.time_slot && activeRule.time_slot.start ? activeRule.time_slot.start : ''; + var endTime = activeRule.time_slot && activeRule.time_slot.end ? activeRule.time_slot.end : ''; + + var uploadLimit = activeRule.wide_tx_rate_limit || 0; + var downloadLimit = activeRule.wide_rx_rate_limit || 0; + + // 时间段和限速值放在同一行 + var timeSlotText = startTime + '-' + endTime; + var limitsText = []; + // 即使限速是0也显示 + limitsText.push('↑' + (uploadLimit > 0 ? formatByterate(uploadLimit, speedUnit) : _('Unlimited'))); + limitsText.push('↓' + (downloadLimit > 0 ? formatByterate(downloadLimit, speedUnit) : _('Unlimited'))); + + // 生效规则的时间段和限速值 + var activeTimeInfo = E('div', { + 'class': 'device-card-rules-active-time' + }, [ + E('span', {}, '● ' + timeSlotText), + E('span', { 'style': 'opacity: 0.8; font-size: 0.7rem; margin-left: 8px;' }, limitsText.join(' ')) + ]); + + if (activeRules.length > 1) { + activeTimeInfo.appendChild(E('div', { + 'class': 'device-card-rules-more' + }, '+' + (activeRules.length - 1) + ' ' + _('more'))); + } + + rulesContent.appendChild(activeTimeInfo); + } else { + rulesContent.appendChild(E('div', { + 'class': 'device-card-rules-inactive' + }, _('No active rule'))); + } + + return E('div', { 'class': 'device-card-section device-card-rules' }, [ + E('div', { 'class': 'device-card-section-label' }, _('Schedule Rules')), + rulesContent + ]); + })(), + // LAN流量(直接显示,不需要展开/收起) + E('div', { 'class': 'device-card-section', 'style': 'margin-top: 12px; padding-top: 12px; border-top: 1px solid rgba(0, 0, 0, 0.1);' }, [ + E('div', { 'class': 'device-card-section-label' }, _('LAN Traffic')), + E('div', { 'class': 'device-card-traffic' }, [ + E('div', { 'class': 'device-card-traffic-row' }, [ + E('span', { 'style': 'color: #f97316; font-size: 0.75rem; font-weight: bold;' }, '↑'), + E('span', { 'style': 'font-weight: 600;' }, formatByterate(device.local_tx_rate || 0, speedUnit)), + E('span', { 'style': 'font-size: 0.75rem; opacity: 0.7;' }, '(' + formatSize(device.local_tx_bytes || 0) + ')') + ]), + E('div', { 'class': 'device-card-traffic-row' }, [ + E('span', { 'style': 'color: #06b6d4; font-size: 0.75rem; font-weight: bold;' }, '↓'), + E('span', { 'style': 'font-weight: 600;' }, formatByterate(device.local_rx_rate || 0, speedUnit)), + E('span', { 'style': 'font-size: 0.75rem; opacity: 0.7;' }, '(' + formatSize(device.local_rx_bytes || 0) + ')') ]) ]) - ]), - // 展开/收起按钮 - E('button', { 'class': 'device-card-toggle' }, _('Show More')) + ]) ]); - // 检查并恢复展开状态 - if (expandedDeviceCards.has(device.mac)) { - card.classList.add('expanded'); - } - - // 绑定展开/收起功能 - var toggleBtn = card.querySelector('.device-card-toggle'); - toggleBtn.textContent = card.classList.contains('expanded') ? _('Show Less') : _('Show More'); - toggleBtn.addEventListener('click', function() { - card.classList.toggle('expanded'); - var isExpanded = card.classList.contains('expanded'); - toggleBtn.textContent = isExpanded ? _('Show Less') : _('Show More'); - // 保存展开状态 - if (isExpanded) { - expandedDeviceCards.add(device.mac); - } else { - expandedDeviceCards.delete(device.mac); - } - }); - cardsContainer.appendChild(card); }); @@ -3206,9 +3976,20 @@ function downsampleForMobile(data, labels, upSeries, downSeries, maxPoints) { // 轮询获取数据 poll.add(updateDeviceData, 1); + + // 轮询获取定时限速规则(每5秒) + poll.add(function() { + return fetchAllScheduleRules().then(function() { + // 规则更新后,重新渲染表格以显示最新的规则状态 + if (window.__bandixRenderTable) { + window.__bandixRenderTable(); + } + }); + }, 5000); // 立即执行一次,不等待轮询 updateDeviceData(); + fetchAllScheduleRules(); // 自动适应主题背景色和文字颜色的函数(仅应用于弹窗和 tooltip) function applyThemeColors() { diff --git a/luci-app-bandix/po/es/bandix.po b/luci-app-bandix/po/es/bandix.po index 968b2d0..e965242 100644 --- a/luci-app-bandix/po/es/bandix.po +++ b/luci-app-bandix/po/es/bandix.po @@ -751,3 +751,93 @@ msgstr "Mostrar más" msgid "Show Less" msgstr "Mostrar menos" + +msgid "Add Schedule Rule" +msgstr "Agregar regla de limitación programada" + +msgid "Add Rule" +msgstr "Agregar regla" + +msgid "Set rate limit rules for different time periods" +msgstr "Establecer reglas de limitación para diferentes períodos de tiempo" + +msgid "Time Slot" +msgstr "Franja horaria" + +msgid "Days of Week" +msgstr "Días de la semana" + +msgid "Mon" +msgstr "Lun" + +msgid "Tue" +msgstr "Mar" + +msgid "Wed" +msgstr "Mié" + +msgid "Thu" +msgstr "Jue" + +msgid "Fri" +msgstr "Vie" + +msgid "Sat" +msgstr "Sáb" + +msgid "Sun" +msgstr "Dom" + +msgid "Adding..." +msgstr "Agregando..." + +msgid "Failed to add schedule rule: " +msgstr "Error al agregar regla programada: " + +msgid "Please set time slot" +msgstr "Por favor establezca la franja horaria" + +msgid "Please select at least one day" +msgstr "Por favor seleccione al menos un día" + +msgid "No scheduled rules yet, click \"Add Rule\" to start setting" +msgstr "Aún no hay reglas programadas, haga clic en \"Agregar regla\" para comenzar" + +msgid "Loading..." +msgstr "Cargando..." + +msgid "No schedule rules" +msgstr "No hay reglas programadas" + +msgid "Failed to load schedule rules" +msgstr "Error al cargar reglas programadas" + +msgid "Delete Schedule Rule" +msgstr "Eliminar regla programada" + +msgid "Are you sure you want to delete this schedule rule?" +msgstr "¿Está seguro de que desea eliminar esta regla programada?" + +msgid "Failed to delete schedule rule" +msgstr "Error al eliminar regla programada" + +msgid "Confirm" +msgstr "Confirmar" + +msgid "Close" +msgstr "Cerrar" + +msgid "rule" +msgstr "regla" + +msgid "rules" +msgstr "reglas" + +msgid "more" +msgstr "más" + +msgid "No active rule" +msgstr "Sin regla activa" + +msgid "Schedule Rules" +msgstr "Reglas de limitación programada" diff --git a/luci-app-bandix/po/fr/bandix.po b/luci-app-bandix/po/fr/bandix.po index 43b2bb8..658f76c 100644 --- a/luci-app-bandix/po/fr/bandix.po +++ b/luci-app-bandix/po/fr/bandix.po @@ -750,4 +750,94 @@ msgid "Show More" msgstr "Afficher plus" msgid "Show Less" -msgstr "Afficher moins" \ No newline at end of file +msgstr "Afficher moins" + +msgid "Add Schedule Rule" +msgstr "Ajouter une règle de limitation programmée" + +msgid "Add Rule" +msgstr "Ajouter une règle" + +msgid "Set rate limit rules for different time periods" +msgstr "Définir des règles de limitation pour différentes périodes" + +msgid "Time Slot" +msgstr "Créneau horaire" + +msgid "Days of Week" +msgstr "Jours de la semaine" + +msgid "Mon" +msgstr "Lun" + +msgid "Tue" +msgstr "Mar" + +msgid "Wed" +msgstr "Mer" + +msgid "Thu" +msgstr "Jeu" + +msgid "Fri" +msgstr "Ven" + +msgid "Sat" +msgstr "Sam" + +msgid "Sun" +msgstr "Dim" + +msgid "Adding..." +msgstr "Ajout en cours..." + +msgid "Failed to add schedule rule: " +msgstr "Échec de l'ajout de la règle programmée : " + +msgid "Please set time slot" +msgstr "Veuillez définir un créneau horaire" + +msgid "Please select at least one day" +msgstr "Veuillez sélectionner au moins un jour" + +msgid "No scheduled rules yet, click \"Add Rule\" to start setting" +msgstr "Aucune règle programmée pour le moment, cliquez sur \"Ajouter une règle\" pour commencer" + +msgid "Loading..." +msgstr "Chargement..." + +msgid "No schedule rules" +msgstr "Aucune règle programmée" + +msgid "Failed to load schedule rules" +msgstr "Échec du chargement des règles programmées" + +msgid "Delete Schedule Rule" +msgstr "Supprimer la règle programmée" + +msgid "Are you sure you want to delete this schedule rule?" +msgstr "Êtes-vous sûr de vouloir supprimer cette règle programmée ?" + +msgid "Failed to delete schedule rule" +msgstr "Échec de la suppression de la règle programmée" + +msgid "Confirm" +msgstr "Confirmer" + +msgid "Close" +msgstr "Fermer" + +msgid "rule" +msgstr "règle" + +msgid "rules" +msgstr "règles" + +msgid "more" +msgstr "plus" + +msgid "No active rule" +msgstr "Aucune règle active" + +msgid "Schedule Rules" +msgstr "Règles de limitation programmée" \ No newline at end of file diff --git a/luci-app-bandix/po/id/bandix.po b/luci-app-bandix/po/id/bandix.po index 968b2b3..0c70d35 100644 --- a/luci-app-bandix/po/id/bandix.po +++ b/luci-app-bandix/po/id/bandix.po @@ -751,3 +751,93 @@ msgstr "Tampilkan lebih banyak" msgid "Show Less" msgstr "Tampilkan lebih sedikit" + +msgid "Add Schedule Rule" +msgstr "Tambah aturan pembatasan terjadwal" + +msgid "Add Rule" +msgstr "Tambah aturan" + +msgid "Set rate limit rules for different time periods" +msgstr "Tetapkan aturan pembatasan untuk periode waktu yang berbeda" + +msgid "Time Slot" +msgstr "Slot waktu" + +msgid "Days of Week" +msgstr "Hari dalam seminggu" + +msgid "Mon" +msgstr "Sen" + +msgid "Tue" +msgstr "Sel" + +msgid "Wed" +msgstr "Rab" + +msgid "Thu" +msgstr "Kam" + +msgid "Fri" +msgstr "Jum" + +msgid "Sat" +msgstr "Sab" + +msgid "Sun" +msgstr "Min" + +msgid "Adding..." +msgstr "Menambahkan..." + +msgid "Failed to add schedule rule: " +msgstr "Gagal menambahkan aturan terjadwal: " + +msgid "Please set time slot" +msgstr "Silakan atur slot waktu" + +msgid "Please select at least one day" +msgstr "Silakan pilih setidaknya satu hari" + +msgid "No scheduled rules yet, click \"Add Rule\" to start setting" +msgstr "Belum ada aturan terjadwal, klik \"Tambah aturan\" untuk mulai pengaturan" + +msgid "Loading..." +msgstr "Memuat..." + +msgid "No schedule rules" +msgstr "Tidak ada aturan terjadwal" + +msgid "Failed to load schedule rules" +msgstr "Gagal memuat aturan terjadwal" + +msgid "Delete Schedule Rule" +msgstr "Hapus aturan pembatasan terjadwal" + +msgid "Are you sure you want to delete this schedule rule?" +msgstr "Apakah Anda yakin ingin menghapus aturan terjadwal ini?" + +msgid "Failed to delete schedule rule" +msgstr "Gagal menghapus aturan terjadwal" + +msgid "Confirm" +msgstr "Konfirmasi" + +msgid "Close" +msgstr "Tutup" + +msgid "rule" +msgstr "aturan" + +msgid "rules" +msgstr "aturan" + +msgid "more" +msgstr "lainnya" + +msgid "No active rule" +msgstr "Tidak ada aturan aktif" + +msgid "Schedule Rules" +msgstr "Aturan pembatasan terjadwal" diff --git a/luci-app-bandix/po/ja/bandix.po b/luci-app-bandix/po/ja/bandix.po index c9a056a..7a1c54b 100644 --- a/luci-app-bandix/po/ja/bandix.po +++ b/luci-app-bandix/po/ja/bandix.po @@ -751,3 +751,93 @@ msgstr "もっと見る" msgid "Show Less" msgstr "閉じる" + +msgid "Add Schedule Rule" +msgstr "スケジュール制限ルールを追加" + +msgid "Add Rule" +msgstr "ルールを追加" + +msgid "Set rate limit rules for different time periods" +msgstr "異なる時間帯にレート制限ルールを設定" + +msgid "Time Slot" +msgstr "時間帯" + +msgid "Days of Week" +msgstr "曜日" + +msgid "Mon" +msgstr "月" + +msgid "Tue" +msgstr "火" + +msgid "Wed" +msgstr "水" + +msgid "Thu" +msgstr "木" + +msgid "Fri" +msgstr "金" + +msgid "Sat" +msgstr "土" + +msgid "Sun" +msgstr "日" + +msgid "Adding..." +msgstr "追加中..." + +msgid "Failed to add schedule rule: " +msgstr "スケジュール制限ルールの追加に失敗しました:" + +msgid "Please set time slot" +msgstr "時間帯を設定してください" + +msgid "Please select at least one day" +msgstr "少なくとも1日を選択してください" + +msgid "No scheduled rules yet, click \"Add Rule\" to start setting" +msgstr "スケジュール制限ルールがまだありません。「ルールを追加」をクリックして設定を開始してください" + +msgid "Loading..." +msgstr "読み込み中..." + +msgid "No schedule rules" +msgstr "スケジュール制限ルールなし" + +msgid "Failed to load schedule rules" +msgstr "スケジュール制限ルールの読み込みに失敗しました" + +msgid "Delete Schedule Rule" +msgstr "スケジュール制限ルールを削除" + +msgid "Are you sure you want to delete this schedule rule?" +msgstr "このスケジュール制限ルールを削除してもよろしいですか?" + +msgid "Failed to delete schedule rule" +msgstr "スケジュール制限ルールの削除に失敗しました" + +msgid "Confirm" +msgstr "確認" + +msgid "Close" +msgstr "閉じる" + +msgid "rule" +msgstr "ルール" + +msgid "rules" +msgstr "ルール" + +msgid "more" +msgstr "その他" + +msgid "No active rule" +msgstr "アクティブなルールなし" + +msgid "Schedule Rules" +msgstr "スケジュール制限ルール" diff --git a/luci-app-bandix/po/pl/bandix.po b/luci-app-bandix/po/pl/bandix.po index 8298f37..5abe740 100644 --- a/luci-app-bandix/po/pl/bandix.po +++ b/luci-app-bandix/po/pl/bandix.po @@ -752,3 +752,93 @@ msgstr "Pokaż więcej" msgid "Show Less" msgstr "Pokaż mniej" +msgid "Add Schedule Rule" +msgstr "Dodaj regułę limitu czasu" + +msgid "Add Rule" +msgstr "Dodaj regułę" + +msgid "Set rate limit rules for different time periods" +msgstr "Ustaw reguły limitu dla różnych okresów czasu" + +msgid "Time Slot" +msgstr "Przedział czasowy" + +msgid "Days of Week" +msgstr "Dni tygodnia" + +msgid "Mon" +msgstr "Pon" + +msgid "Tue" +msgstr "Wt" + +msgid "Wed" +msgstr "Śr" + +msgid "Thu" +msgstr "Czw" + +msgid "Fri" +msgstr "Pt" + +msgid "Sat" +msgstr "Sob" + +msgid "Sun" +msgstr "Nie" + +msgid "Adding..." +msgstr "Dodawanie..." + +msgid "Failed to add schedule rule: " +msgstr "Nie udało się dodać reguły limitu czasu: " + +msgid "Please set time slot" +msgstr "Proszę ustawić przedział czasowy" + +msgid "Please select at least one day" +msgstr "Proszę wybrać co najmniej jeden dzień" + +msgid "No scheduled rules yet, click \"Add Rule\" to start setting" +msgstr "Brak jeszcze reguł limitu czasu, kliknij \"Dodaj regułę\", aby rozpocząć" + +msgid "Loading..." +msgstr "Ładowanie..." + +msgid "No schedule rules" +msgstr "Brak reguł limitu czasu" + +msgid "Failed to load schedule rules" +msgstr "Nie udało się załadować reguł limitu czasu" + +msgid "Delete Schedule Rule" +msgstr "Usuń regułę limitu czasu" + +msgid "Are you sure you want to delete this schedule rule?" +msgstr "Czy na pewno chcesz usunąć tę regułę limitu czasu?" + +msgid "Failed to delete schedule rule" +msgstr "Nie udało się usunąć reguły limitu czasu" + +msgid "Confirm" +msgstr "Potwierdź" + +msgid "Close" +msgstr "Zamknij" + +msgid "rule" +msgstr "reguła" + +msgid "rules" +msgstr "reguły" + +msgid "more" +msgstr "więcej" + +msgid "No active rule" +msgstr "Brak aktywnej reguły" + +msgid "Schedule Rules" +msgstr "Reguły limitu czasu" + diff --git a/luci-app-bandix/po/ru/bandix.po b/luci-app-bandix/po/ru/bandix.po index 4714dea..29401e5 100644 --- a/luci-app-bandix/po/ru/bandix.po +++ b/luci-app-bandix/po/ru/bandix.po @@ -750,4 +750,94 @@ msgid "Show More" msgstr "Показать больше" msgid "Show Less" -msgstr "Показать меньше" \ No newline at end of file +msgstr "Показать меньше" + +msgid "Add Schedule Rule" +msgstr "Добавить правило ограничения по расписанию" + +msgid "Add Rule" +msgstr "Добавить правило" + +msgid "Set rate limit rules for different time periods" +msgstr "Установить правила ограничения скорости для разных периодов времени" + +msgid "Time Slot" +msgstr "Временной интервал" + +msgid "Days of Week" +msgstr "Дни недели" + +msgid "Mon" +msgstr "Пн" + +msgid "Tue" +msgstr "Вт" + +msgid "Wed" +msgstr "Ср" + +msgid "Thu" +msgstr "Чт" + +msgid "Fri" +msgstr "Пт" + +msgid "Sat" +msgstr "Сб" + +msgid "Sun" +msgstr "Вс" + +msgid "Adding..." +msgstr "Добавление..." + +msgid "Failed to add schedule rule: " +msgstr "Не удалось добавить правило расписания: " + +msgid "Please set time slot" +msgstr "Пожалуйста, установите временной интервал" + +msgid "Please select at least one day" +msgstr "Пожалуйста, выберите хотя бы один день" + +msgid "No scheduled rules yet, click \"Add Rule\" to start setting" +msgstr "Правил расписания пока нет, нажмите \"Добавить правило\", чтобы начать настройку" + +msgid "Loading..." +msgstr "Загрузка..." + +msgid "No schedule rules" +msgstr "Нет правил расписания" + +msgid "Failed to load schedule rules" +msgstr "Не удалось загрузить правила расписания" + +msgid "Delete Schedule Rule" +msgstr "Удалить правило ограничения по расписанию" + +msgid "Are you sure you want to delete this schedule rule?" +msgstr "Вы уверены, что хотите удалить это правило расписания?" + +msgid "Failed to delete schedule rule" +msgstr "Не удалось удалить правило расписания" + +msgid "Confirm" +msgstr "Подтвердить" + +msgid "Close" +msgstr "Закрыть" + +msgid "rule" +msgstr "правило" + +msgid "rules" +msgstr "правила" + +msgid "more" +msgstr "еще" + +msgid "No active rule" +msgstr "Нет активных правил" + +msgid "Schedule Rules" +msgstr "Правила ограничения по расписанию" \ No newline at end of file diff --git a/luci-app-bandix/po/th/bandix.po b/luci-app-bandix/po/th/bandix.po index d2d9ea2..aed52d4 100644 --- a/luci-app-bandix/po/th/bandix.po +++ b/luci-app-bandix/po/th/bandix.po @@ -751,3 +751,93 @@ msgstr "แสดงเพิ่มเติม" msgid "Show Less" msgstr "แสดงน้อยลง" + +msgid "Add Schedule Rule" +msgstr "เพิ่มกฎการจำกัดอัตราแบบกำหนดเวลา" + +msgid "Add Rule" +msgstr "เพิ่มกฎ" + +msgid "Set rate limit rules for different time periods" +msgstr "ตั้งค่ากฎการจำกัดอัตราสำหรับช่วงเวลาต่างๆ" + +msgid "Time Slot" +msgstr "ช่วงเวลา" + +msgid "Days of Week" +msgstr "วันในสัปดาห์" + +msgid "Mon" +msgstr "จ" + +msgid "Tue" +msgstr "อ" + +msgid "Wed" +msgstr "พ" + +msgid "Thu" +msgstr "พฤ" + +msgid "Fri" +msgstr "ศ" + +msgid "Sat" +msgstr "ส" + +msgid "Sun" +msgstr "อา" + +msgid "Adding..." +msgstr "กำลังเพิ่ม..." + +msgid "Failed to add schedule rule: " +msgstr "เพิ่มกฎการจำกัดอัตราแบบกำหนดเวลาล้มเหลว: " + +msgid "Please set time slot" +msgstr "โปรดตั้งค่าช่วงเวลา" + +msgid "Please select at least one day" +msgstr "โปรดเลือกอย่างน้อยหนึ่งวัน" + +msgid "No scheduled rules yet, click \"Add Rule\" to start setting" +msgstr "ยังไม่มีกฎการจำกัดอัตราแบบกำหนดเวลา คลิก \"เพิ่มกฎ\" เพื่อเริ่มตั้งค่า" + +msgid "Loading..." +msgstr "กำลังโหลด..." + +msgid "No schedule rules" +msgstr "ไม่มีกฎการจำกัดอัตราแบบกำหนดเวลา" + +msgid "Failed to load schedule rules" +msgstr "โหลดกฎการจำกัดอัตราแบบกำหนดเวลาล้มเหลว" + +msgid "Delete Schedule Rule" +msgstr "ลบกฎการจำกัดอัตราแบบกำหนดเวลา" + +msgid "Are you sure you want to delete this schedule rule?" +msgstr "คุณแน่ใจหรือไม่ว่าต้องการลบกฎการจำกัดอัตราแบบกำหนดเวลานี้?" + +msgid "Failed to delete schedule rule" +msgstr "ลบกฎการจำกัดอัตราแบบกำหนดเวลาล้มเหลว" + +msgid "Confirm" +msgstr "ยืนยัน" + +msgid "Close" +msgstr "ปิด" + +msgid "rule" +msgstr "กฎ" + +msgid "rules" +msgstr "กฎ" + +msgid "more" +msgstr "เพิ่มเติม" + +msgid "No active rule" +msgstr "ไม่มีกฎที่ใช้งาน" + +msgid "Schedule Rules" +msgstr "กฎการจำกัดอัตราแบบกำหนดเวลา" diff --git a/luci-app-bandix/po/zh_Hans/bandix.po b/luci-app-bandix/po/zh_Hans/bandix.po index b89b0cc..42859e0 100644 --- a/luci-app-bandix/po/zh_Hans/bandix.po +++ b/luci-app-bandix/po/zh_Hans/bandix.po @@ -750,4 +750,94 @@ msgid "Show More" msgstr "显示更多" msgid "Show Less" -msgstr "显示更少" \ No newline at end of file +msgstr "显示更少" + +msgid "Add Schedule Rule" +msgstr "添加定时限速规则" + +msgid "Add Rule" +msgstr "添加规则" + +msgid "Set rate limit rules for different time periods" +msgstr "为不同时间段设置限速规则" + +msgid "Time Slot" +msgstr "时间段" + +msgid "Days of Week" +msgstr "星期" + +msgid "Mon" +msgstr "周一" + +msgid "Tue" +msgstr "周二" + +msgid "Wed" +msgstr "周三" + +msgid "Thu" +msgstr "周四" + +msgid "Fri" +msgstr "周五" + +msgid "Sat" +msgstr "周六" + +msgid "Sun" +msgstr "周日" + +msgid "Adding..." +msgstr "添加中..." + +msgid "Failed to add schedule rule: " +msgstr "添加定时限速规则失败:" + +msgid "Please set time slot" +msgstr "请设置时间段" + +msgid "Please select at least one day" +msgstr "请至少选择一天" + +msgid "No scheduled rules yet, click \"Add Rule\" to start setting" +msgstr "还没有定时限速规则,点击"添加规则"开始设置" + +msgid "Loading..." +msgstr "加载中..." + +msgid "No schedule rules" +msgstr "没有定时限速规则" + +msgid "Failed to load schedule rules" +msgstr "加载定时限速规则失败" + +msgid "Delete Schedule Rule" +msgstr "删除定时限速规则" + +msgid "Are you sure you want to delete this schedule rule?" +msgstr "确定要删除这个定时限速规则吗?" + +msgid "Failed to delete schedule rule" +msgstr "删除定时限速规则失败" + +msgid "Confirm" +msgstr "确认" + +msgid "Close" +msgstr "关闭" + +msgid "rule" +msgstr "规则" + +msgid "rules" +msgstr "规则" + +msgid "more" +msgstr "更多" + +msgid "No active rule" +msgstr "无生效规则" + +msgid "Schedule Rules" +msgstr "定时限速规则" \ No newline at end of file diff --git a/luci-app-bandix/po/zh_Hant/bandix.po b/luci-app-bandix/po/zh_Hant/bandix.po index d2c43d7..ec598d2 100644 --- a/luci-app-bandix/po/zh_Hant/bandix.po +++ b/luci-app-bandix/po/zh_Hant/bandix.po @@ -750,4 +750,94 @@ msgid "Show More" msgstr "顯示更多" msgid "Show Less" -msgstr "顯示更少" \ No newline at end of file +msgstr "顯示更少" + +msgid "Add Schedule Rule" +msgstr "新增定時限速規則" + +msgid "Add Rule" +msgstr "新增規則" + +msgid "Set rate limit rules for different time periods" +msgstr "為不同時間段設定限速規則" + +msgid "Time Slot" +msgstr "時間段" + +msgid "Days of Week" +msgstr "星期" + +msgid "Mon" +msgstr "週一" + +msgid "Tue" +msgstr "週二" + +msgid "Wed" +msgstr "週三" + +msgid "Thu" +msgstr "週四" + +msgid "Fri" +msgstr "週五" + +msgid "Sat" +msgstr "週六" + +msgid "Sun" +msgstr "週日" + +msgid "Adding..." +msgstr "新增中..." + +msgid "Failed to add schedule rule: " +msgstr "新增定時限速規則失敗:" + +msgid "Please set time slot" +msgstr "請設定時間段" + +msgid "Please select at least one day" +msgstr "請至少選擇一天" + +msgid "No scheduled rules yet, click \"Add Rule\" to start setting" +msgstr "還沒有定時限速規則,點擊"新增規則"開始設定" + +msgid "Loading..." +msgstr "載入中..." + +msgid "No schedule rules" +msgstr "沒有定時限速規則" + +msgid "Failed to load schedule rules" +msgstr "載入定時限速規則失敗" + +msgid "Delete Schedule Rule" +msgstr "刪除定時限速規則" + +msgid "Are you sure you want to delete this schedule rule?" +msgstr "確定要刪除這個定時限速規則嗎?" + +msgid "Failed to delete schedule rule" +msgstr "刪除定時限速規則失敗" + +msgid "Confirm" +msgstr "確認" + +msgid "Close" +msgstr "關閉" + +msgid "rule" +msgstr "規則" + +msgid "rules" +msgstr "規則" + +msgid "more" +msgstr "更多" + +msgid "No active rule" +msgstr "無生效規則" + +msgid "Schedule Rules" +msgstr "定時限速規則" \ No newline at end of file diff --git a/luci-app-bandix/root/usr/libexec/rpcd/luci.bandix b/luci-app-bandix/root/usr/libexec/rpcd/luci.bandix index 645db55..4459919 100755 --- a/luci-app-bandix/root/usr/libexec/rpcd/luci.bandix +++ b/luci-app-bandix/root/usr/libexec/rpcd/luci.bandix @@ -12,6 +12,7 @@ readonly BANDIX_METRICS_API="$BANDIX_API_BASE/api/traffic/metrics" readonly BANDIX_CONNECTION_API="$BANDIX_API_BASE/api/connection/devices" readonly BANDIX_DNS_QUERIES_API="$BANDIX_API_BASE/api/dns/queries" readonly BANDIX_DNS_STATS_API="$BANDIX_API_BASE/api/dns/stats" +readonly BANDIX_SCHEDULE_LIMITS_API="$BANDIX_API_BASE/api/traffic/limits/schedule" # 通用函数:创建简单的JSON响应 make_value() { @@ -98,56 +99,6 @@ get_metrics() { fi } -# 设置设备限速 -set_device_rate_limit() { - local mac="$1" - local wide_tx_rate_limit="$2" - local wide_rx_rate_limit="$3" - - # logger "luci.bandix: set_device_rate_limit 参数: mac=$mac tx=$wide_tx_rate_limit rx=$wide_rx_rate_limit" - - # 验证参数 - if [ -z "$mac" ]; then - make_error "MAC address is required" - return - fi - - # 转义MAC地址中的特殊字符 - local mac_escaped=$(escape_json_string "$mac") - - # 构建请求数据 - local request_data="{ - \"mac\": \"$mac\", - \"wide_tx_rate_limit\": $wide_tx_rate_limit, - \"wide_rx_rate_limit\": $wide_rx_rate_limit - }" - - # 记录请求数据(用于调试) - # logger "luci.bandix: request_data=$request_data" - - # 发送请求到bandix API - local response=$(curl -s --connect-timeout 3 --max-time 10 -X POST -H "Content-Type: application/json" -d "$request_data" "$BANDIX_LIMITS_API" 2>/dev/null) - - # logger "luci.bandix: API response=$response" - - if [ $? -eq 0 ] && [ -n "$response" ]; then - # 检查API响应是否包含success状态 - if echo "$response" | grep -q '"status":\s*"success"'; then - make_success "Rate limit set successfully" - else - # API返回了响应但不是success状态 - json_init - json_add_boolean "success" 0 - json_add_string "error" "API returned error response" - json_add_string "response" "$response" - json_dump - json_cleanup - fi - else - make_error "Failed to set rate limit" - fi -} - # 设置设备主机名绑定 set_device_hostname() { local mac="$1" @@ -293,6 +244,129 @@ get_dns_stats() { echo "$response" } +# 获取定时限速规则 +get_schedule_limits() { + # 从 Bandix API 获取定时限速规则 + local response=$(curl -s --connect-timeout 2 --max-time 5 -X GET "$BANDIX_SCHEDULE_LIMITS_API" 2>/dev/null) + + # 检查API调用是否成功 + if [ $? -ne 0 ] || [ -z "$response" ]; then + make_error "Failed to connect to Bandix service" + return + fi + + # 直接返回API结果 + echo "$response" +} + +# 设置定时限速规则 +set_schedule_limit() { + local mac="$1" + local start_time="$2" + local end_time="$3" + local days="$4" + local wide_tx_rate_limit="$5" + local wide_rx_rate_limit="$6" + + # 验证参数 + if [ -z "$mac" ]; then + make_error "MAC address is required" + return + fi + + if [ -z "$start_time" ] || [ -z "$end_time" ] || [ -z "$days" ]; then + make_error "Time slot parameters are required" + return + fi + + # 转义MAC地址中的特殊字符 + local mac_escaped=$(escape_json_string "$mac") + + # 构建请求数据 + local request_data="{ + \"mac\": \"$mac\", + \"time_slot\": { + \"start\": \"$start_time\", + \"end\": \"$end_time\", + \"days\": $days + }, + \"wide_tx_rate_limit\": $wide_tx_rate_limit, + \"wide_rx_rate_limit\": $wide_rx_rate_limit + }" + + # 发送请求到bandix API + local response=$(curl -s --connect-timeout 3 --max-time 10 -X POST -H "Content-Type: application/json" -d "$request_data" "$BANDIX_SCHEDULE_LIMITS_API" 2>/dev/null) + + if [ $? -eq 0 ] && [ -n "$response" ]; then + # 检查API响应是否包含success状态 + if echo "$response" | grep -q '"status":\s*"success"'; then + make_success "Schedule limit set successfully" + else + # API返回了响应但不是success状态 + json_init + json_add_boolean "success" 0 + json_add_string "error" "API returned error response" + json_add_string "response" "$response" + json_dump + json_cleanup + fi + else + make_error "Failed to set schedule limit" + fi +} + +# 删除定时限速规则 +delete_schedule_limit() { + local mac="$1" + local start_time="$2" + local end_time="$3" + local days="$4" + + # 验证参数 + if [ -z "$mac" ]; then + make_error "MAC address is required" + return + fi + + if [ -z "$start_time" ] || [ -z "$end_time" ] || [ -z "$days" ]; then + make_error "Time slot parameters are required" + return + fi + + # 转义MAC地址中的特殊字符 + local mac_escaped=$(escape_json_string "$mac") + + # 构建请求数据 + local request_data="{ + \"mac\": \"$mac\", + \"time_slot\": { + \"start\": \"$start_time\", + \"end\": \"$end_time\", + \"days\": $days + } + }" + + # 发送请求到bandix API + local response=$(curl -s --connect-timeout 3 --max-time 10 -X DELETE -H "Content-Type: application/json" -d "$request_data" "$BANDIX_SCHEDULE_LIMITS_API" 2>/dev/null) + + if [ $? -eq 0 ] && [ -n "$response" ]; then + # 检查API响应是否包含success状态 + if echo "$response" | grep -q '"status":\s*"success"'; then + make_success "Schedule limit deleted successfully" + else + # API返回了响应但不是success状态 + json_init + json_add_boolean "success" 0 + json_add_string "error" "API returned error response" + json_add_string "response" "$response" + json_dump + json_cleanup + fi + else + make_error "Failed to delete schedule limit" + fi +} + case "$1" in list) json_init @@ -303,12 +377,6 @@ case "$1" in json_add_string "mac" json_close_object - json_add_object "setRateLimit" - json_add_string "mac" - json_add_int "wide_rx_rate_limit" - json_add_int "wide_tx_rate_limit" - json_close_object - json_add_object "setHostname" json_add_string "mac" json_add_string "hostname" @@ -330,6 +398,25 @@ case "$1" in json_add_object "getDnsStats" json_close_object + json_add_object "getScheduleLimits" + json_close_object + + json_add_object "setScheduleLimit" + json_add_string "mac" + json_add_string "start_time" + json_add_string "end_time" + json_add_string "days" + json_add_int "wide_rx_rate_limit" + json_add_int "wide_tx_rate_limit" + json_close_object + + json_add_object "deleteScheduleLimit" + json_add_string "mac" + json_add_string "start_time" + json_add_string "end_time" + json_add_string "days" + json_close_object + json_dump json_cleanup ;; @@ -360,42 +447,6 @@ case "$1" in fi get_metrics "$mac" ;; - setRateLimit) - # logger "luci.bandix: setRateLimit called" - - # 从 stdin 读取 JSON 参数 - read input - # logger "luci.bandix: received input: $input" - - if [ -n "$input" ]; then - # 尝试解析数组格式的 JSON 参数 (LuCI RPC 通常使用数组格式) - # 参数格式: ["mac", wide_tx_rate_limit, wide_rx_rate_limit] - mac=$(echo "$input" | jsonfilter -e '$[0]' 2>/dev/null) - wide_tx_rate_limit=$(echo "$input" | jsonfilter -e '$[1]' 2>/dev/null) - wide_rx_rate_limit=$(echo "$input" | jsonfilter -e '$[2]' 2>/dev/null) - - # 如果数组格式解析失败,尝试对象格式 - if [ -z "$mac" ]; then - mac=$(echo "$input" | jsonfilter -e '$.mac' 2>/dev/null) - wide_tx_rate_limit=$(echo "$input" | jsonfilter -e '$.wide_tx_rate_limit' 2>/dev/null) - wide_rx_rate_limit=$(echo "$input" | jsonfilter -e '$.wide_rx_rate_limit' 2>/dev/null) - fi - - # logger "luci.bandix: parsed - mac=$mac tx=$wide_tx_rate_limit rx=$wide_rx_rate_limit" - - # 验证参数 - if [ -n "$mac" ] && [ -n "$wide_tx_rate_limit" ] && [ -n "$wide_rx_rate_limit" ]; then - # 调用限速设置函数 - set_device_rate_limit "$mac" "$wide_tx_rate_limit" "$wide_rx_rate_limit" - else - # logger "luci.bandix: setRateLimit 参数缺失或无效" - make_error "Missing or invalid parameters" - fi - else - # logger "luci.bandix: setRateLimit 没有接收到输入" - make_error "No input received" - fi - ;; setHostname) # logger "luci.bandix: setHostname called" @@ -471,6 +522,122 @@ case "$1" in # logger "luci.bandix: getDnsStats called" get_dns_stats ;; + getScheduleLimits) + # logger "luci.bandix: getScheduleLimits called" + get_schedule_limits + ;; + setScheduleLimit) + # logger "luci.bandix: setScheduleLimit called" + + # 从 stdin 读取 JSON 参数 + read input + # logger "luci.bandix: received input: $input" + + if [ -n "$input" ]; then + # 尝试解析数组格式的 JSON 参数 (LuCI RPC 通常使用数组格式) + # 参数格式: ["mac", "start_time", "end_time", "days", wide_tx_rate_limit, wide_rx_rate_limit] + mac=$(echo "$input" | jsonfilter -e '$[0]' 2>/dev/null) + start_time=$(echo "$input" | jsonfilter -e '$[1]' 2>/dev/null) + end_time=$(echo "$input" | jsonfilter -e '$[2]' 2>/dev/null) + # days 可能是 JSON 数组字符串,需要特殊处理 + days=$(echo "$input" | jsonfilter -e '$[3]' 2>/dev/null) + # 如果提取失败,尝试作为字符串提取 + if [ -z "$days" ]; then + days=$(echo "$input" | jsonfilter -e '$[3]' -s 2>/dev/null) + fi + wide_tx_rate_limit=$(echo "$input" | jsonfilter -e '$[4]' 2>/dev/null) + wide_rx_rate_limit=$(echo "$input" | jsonfilter -e '$[5]' 2>/dev/null) + + # 如果数组格式解析失败,尝试对象格式 + if [ -z "$mac" ]; then + mac=$(echo "$input" | jsonfilter -e '$.mac' 2>/dev/null) + start_time=$(echo "$input" | jsonfilter -e '$.start_time' 2>/dev/null) + end_time=$(echo "$input" | jsonfilter -e '$.end_time' 2>/dev/null) + # days 可能是 JSON 数组字符串 + days=$(echo "$input" | jsonfilter -e '$.days' 2>/dev/null) + # 如果提取失败,尝试提取为 JSON 数组 + if [ -z "$days" ]; then + days=$(echo "$input" | jsonfilter -e '$.days' -s 2>/dev/null) + fi + wide_tx_rate_limit=$(echo "$input" | jsonfilter -e '$.wide_tx_rate_limit' 2>/dev/null) + wide_rx_rate_limit=$(echo "$input" | jsonfilter -e '$.wide_rx_rate_limit' 2>/dev/null) + fi + + # 如果 days 还不是有效的 JSON 数组格式,尝试从原始输入中提取 + if [ -z "$days" ] || ! echo "$days" | grep -q '^\['; then + # 尝试直接提取 days 字段的值 + days=$(echo "$input" | sed -n 's/.*"days"[[:space:]]*:[[:space:]]*\(\[.*\]\).*/\1/p' 2>/dev/null) + fi + + # logger "luci.bandix: parsed - mac=$mac start=$start_time end=$end_time days=$days tx=$wide_tx_rate_limit rx=$wide_rx_rate_limit" + + # 验证参数 + if [ -n "$mac" ] && [ -n "$start_time" ] && [ -n "$end_time" ] && [ -n "$days" ] && [ -n "$wide_tx_rate_limit" ] && [ -n "$wide_rx_rate_limit" ]; then + # 调用定时限速设置函数 + set_schedule_limit "$mac" "$start_time" "$end_time" "$days" "$wide_tx_rate_limit" "$wide_rx_rate_limit" + else + # logger "luci.bandix: setScheduleLimit 参数缺失或无效" + make_error "Missing or invalid parameters" + fi + else + # logger "luci.bandix: setScheduleLimit 没有接收到输入" + make_error "No input received" + fi + ;; + deleteScheduleLimit) + # logger "luci.bandix: deleteScheduleLimit called" + + # 从 stdin 读取 JSON 参数 + read input + # logger "luci.bandix: received input: $input" + + if [ -n "$input" ]; then + # 尝试解析数组格式的 JSON 参数 (LuCI RPC 通常使用数组格式) + # 参数格式: ["mac", "start_time", "end_time", "days"] + mac=$(echo "$input" | jsonfilter -e '$[0]' 2>/dev/null) + start_time=$(echo "$input" | jsonfilter -e '$[1]' 2>/dev/null) + end_time=$(echo "$input" | jsonfilter -e '$[2]' 2>/dev/null) + # days 可能是 JSON 数组字符串,需要特殊处理 + days=$(echo "$input" | jsonfilter -e '$[3]' 2>/dev/null) + # 如果提取失败,尝试作为字符串提取 + if [ -z "$days" ]; then + days=$(echo "$input" | jsonfilter -e '$[3]' -s 2>/dev/null) + fi + + # 如果数组格式解析失败,尝试对象格式 + if [ -z "$mac" ]; then + mac=$(echo "$input" | jsonfilter -e '$.mac' 2>/dev/null) + start_time=$(echo "$input" | jsonfilter -e '$.start_time' 2>/dev/null) + end_time=$(echo "$input" | jsonfilter -e '$.end_time' 2>/dev/null) + # days 可能是 JSON 数组字符串 + days=$(echo "$input" | jsonfilter -e '$.days' 2>/dev/null) + # 如果提取失败,尝试提取为 JSON 数组 + if [ -z "$days" ]; then + days=$(echo "$input" | jsonfilter -e '$.days' -s 2>/dev/null) + fi + fi + + # 如果 days 还不是有效的 JSON 数组格式,尝试从原始输入中提取 + if [ -z "$days" ] || ! echo "$days" | grep -q '^\['; then + # 尝试直接提取 days 字段的值 + days=$(echo "$input" | sed -n 's/.*"days"[[:space:]]*:[[:space:]]*\(\[.*\]\).*/\1/p' 2>/dev/null) + fi + + # logger "luci.bandix: parsed - mac=$mac start=$start_time end=$end_time days=$days" + + # 验证参数 + if [ -n "$mac" ] && [ -n "$start_time" ] && [ -n "$end_time" ] && [ -n "$days" ]; then + # 调用定时限速删除函数 + delete_schedule_limit "$mac" "$start_time" "$end_time" "$days" + else + # logger "luci.bandix: deleteScheduleLimit 参数缺失或无效" + make_error "Missing or invalid parameters" + fi + else + # logger "luci.bandix: deleteScheduleLimit 没有接收到输入" + make_error "No input received" + fi + ;; esac ;; esac diff --git a/luci-app-bandix/root/usr/share/rpcd/acl.d/luci-app-bandix.json b/luci-app-bandix/root/usr/share/rpcd/acl.d/luci-app-bandix.json index 4821a00..7cb6cd8 100644 --- a/luci-app-bandix/root/usr/share/rpcd/acl.d/luci-app-bandix.json +++ b/luci-app-bandix/root/usr/share/rpcd/acl.d/luci-app-bandix.json @@ -6,11 +6,13 @@ "luci.bandix": [ "getStatus", "getMetrics", - "setRateLimit", "getConnection", "setHostname", "getDnsQueries", - "getDnsStats" + "getDnsStats", + "getScheduleLimits", + "setScheduleLimit", + "deleteScheduleLimit" ] }, "uci": [ @@ -20,13 +22,15 @@ "write": { "ubus": { "luci.bandix": [ - "setRateLimit", "getStatus", "getMetrics", "getConnection", "setHostname", "getDnsQueries", - "getDnsStats" + "getDnsStats", + "getScheduleLimits", + "setScheduleLimit", + "deleteScheduleLimit" ] }, "uci": [ diff --git a/openlist2/Makefile b/openlist2/Makefile index d74dc3f..b7c3087 100644 --- a/openlist2/Makefile +++ b/openlist2/Makefile @@ -7,13 +7,13 @@ include $(TOPDIR)/rules.mk PKG_NAME:=openlist2 -PKG_VERSION:=4.1.7 -PKG_WEB_VERSION:=4.1.7 +PKG_VERSION:=4.1.8 +PKG_WEB_VERSION:=4.1.8 PKG_RELEASE:=1 PKG_SOURCE:=openlist-$(PKG_VERSION).tar.gz PKG_SOURCE_URL:=https://codeload.github.com/OpenListTeam/OpenList/tar.gz/v$(PKG_VERSION)? -PKG_HASH:=f1b92628be09ba181decc46423c3e0624b78aedfcd28590990a46ba03d75e5e4 +PKG_HASH:=8ba861db0ad29e60fcfb4544ff3744dfa235ab1744ea0c998e1c6f80a630996d PKG_BUILD_DIR:=$(BUILD_DIR)/OpenList-$(PKG_VERSION) @@ -24,7 +24,7 @@ PKG_MAINTAINER:=sbwml define Download/openlist-frontend FILE:=openlist-frontend-dist-lite-v$(PKG_WEB_VERSION).tar.gz URL:=https://github.com/OpenListTeam/OpenList-Frontend/releases/download/v$(PKG_WEB_VERSION)/ - HASH:=2a365c1ce17904926cf275540680f0f24c0c8c3620fcbb98149d7faf781235aa + HASH:=00c8cf73fa98ba3ff56de9a8fd02d545ef87236170e03ca6f9a76c04d4cf957e endef PKG_BUILD_DEPENDS:=golang/host diff --git a/openwrt-bandix/Makefile b/openwrt-bandix/Makefile index 725f200..2a1b626 100644 --- a/openwrt-bandix/Makefile +++ b/openwrt-bandix/Makefile @@ -1,7 +1,7 @@ include $(TOPDIR)/rules.mk PKG_NAME:=bandix -PKG_VERSION:=0.8.3 +PKG_VERSION:=0.9.0 PKG_RELEASE:=1 PKG_LICENSE:=Apache-2.0 @@ -13,7 +13,7 @@ include $(INCLUDE_DIR)/package.mk include $(TOPDIR)/feeds/packages/lang/rust/rust-values.mk # 二进制文件的文件名和URL -RUST_BANDIX_VERSION:=0.8.3 +RUST_BANDIX_VERSION:=0.9.0 RUST_BINARY_FILENAME:=bandix-$(RUST_BANDIX_VERSION)-$(RUSTC_TARGET_ARCH).tar.gz