From 7b5d12b8ecc371f4e38f3867163f1ce68444ce8d Mon Sep 17 00:00:00 2001 From: action Date: Sun, 6 Sep 2026 22:47:17 +0800 Subject: [PATCH] update 2026-09-06 22:47:17 --- luci-app-bandix/Makefile | 2 +- .../resources/view/bandix/index.js | 445 +++++++++++++++++- luci-app-bandix/po/es/bandix.po | 90 ++++ luci-app-bandix/po/fr/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 | 90 ++++ luci-app-bandix/po/zh_Hans/bandix.po | 90 ++++ luci-app-bandix/po/zh_Hant/bandix.po | 90 ++++ .../root/usr/libexec/rpcd/luci.bandix | 134 ++++++ .../usr/share/rpcd/acl.d/luci-app-bandix.json | 10 +- luci-app-clashoo/Makefile | 4 +- .../root/usr/share/rpcd/ucode/luci.clashoo | 4 +- openwrt-bandix/Makefile | 26 +- v2ray-geodata/Makefile | 4 +- 15 files changed, 1229 insertions(+), 30 deletions(-) diff --git a/luci-app-bandix/Makefile b/luci-app-bandix/Makefile index d01c5958..b214a92c 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.12.10 +PKG_VERSION:=0.12.11 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 52f4d209..384f0668 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 @@ -470,6 +470,26 @@ var callSetDefaultRateLimit = rpc.declare({ expect: {} }); +var callGetTrafficQuotas = rpc.declare({ + object: 'luci.bandix', + method: 'getTrafficQuotas', + expect: {} +}); + +var callSetTrafficQuota = rpc.declare({ + object: 'luci.bandix', + method: 'setTrafficQuota', + params: ['mac', 'minute_bytes', 'hourly_bytes', 'daily_bytes', 'weekly_bytes', 'monthly_bytes', 'total_bytes'], + expect: {} +}); + +var callDeleteTrafficQuota = rpc.declare({ + object: 'luci.bandix', + method: 'deleteTrafficQuota', + params: ['mac'], + expect: {} +}); + return view.extend({ load: function () { return Promise.all([ @@ -1902,6 +1922,74 @@ return view.extend({ .schedule-rules-info { } + + .traffic-quota-section { + margin-top: 18px; + padding-top: 16px; + border-top: 1px solid ${scheme === 'dark' ? 'rgba(255,255,255,0.16)' : 'rgba(0,0,0,0.12)'}; + } + .traffic-quota-header { + display: flex; + justify-content: space-between; + align-items: center; + gap: 12px; + margin-bottom: 6px; + } + .traffic-quota-grid { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 10px 14px; + margin-top: 12px; + } + .traffic-quota-input-row { + display: flex; + gap: 6px; + } + .traffic-quota-input-row .form-input { min-width: 0; flex: 1; } + .traffic-quota-input-row .cbi-input-select { width: 82px; flex-shrink: 0; } + .traffic-quota-usage { + margin-top: 12px; + padding: 10px 12px; + border-radius: 6px; + background: ${scheme === 'dark' ? 'rgba(255,255,255,0.06)' : 'rgba(0,0,0,0.04)'}; + font-size: 0.8125rem; + } + .traffic-quota-usage-row { + display: flex; + justify-content: space-between; + gap: 12px; + padding: 2px 0; + } + .traffic-quota-status { + font-size: 0.75rem; + font-weight: 600; + border-radius: 999px; + padding: 2px 8px; + color: #fff; + background: #10b981; + } + .traffic-quota-status.blocked { background: #ef4444; } + .traffic-quota-status.unconfigured { background: #6b7280; } + .traffic-quota-cell { + margin-top: 6px; + padding-top: 6px; + border-top: 1px dashed ${scheme === 'dark' ? 'rgba(255,255,255,0.18)' : 'rgba(0,0,0,0.14)'}; + font-size: 0.75rem; + } + .traffic-quota-blocked-badge { + display: inline-block; + margin-left: 6px; + padding: 1px 6px; + border-radius: 999px; + color: #fff; + background: #ef4444; + font-size: 0.6875rem; + font-weight: 600; + } + + @media (max-width: 600px) { + .traffic-quota-grid { grid-template-columns: 1fr; } + } /* 统计区域样式 */ .traffic-stats-container { @@ -2904,7 +2992,7 @@ return view.extend({ E('th', {}, _('Device Info')), E('th', {}, _('LAN Traffic')), E('th', {}, _('WAN Traffic')), - E('th', {}, _('Rate Limit')), + E('th', {}, _('Rules / Quota')), E('th', {}, _('Actions')) ]) ]), @@ -3171,7 +3259,30 @@ return view.extend({ }); } - // 创建限速设置模态框 + function createQuotaInput(period, label) { + return E('div', { 'class': 'form-group', 'style': 'margin-bottom: 0;' }, [ + E('label', { 'class': 'form-label' }, label), + E('div', { 'class': 'traffic-quota-input-row' }, [ + E('input', { + 'type': 'number', + 'min': '0', + 'step': '0.01', + 'class': 'form-input', + 'id': 'traffic-quota-' + period + '-value', + 'placeholder': '0' + }), + E('select', { 'class': 'cbi-input-select', 'id': 'traffic-quota-' + period + '-unit' }, [ + E('option', { 'value': '1' }, 'B'), + E('option', { 'value': '1024' }, 'KB'), + E('option', { 'value': '1048576' }, 'MB'), + E('option', { 'value': '1073741824' }, 'GB'), + E('option', { 'value': '1099511627776' }, 'TB') + ]) + ]) + ]); + } + + // 创建设备设置模态框 var bandixModal = E('div', { 'class': 'bandix-modal-overlay', 'id': 'rate-limit-bandix-modal' }, [ E('div', { 'class': 'modal-content bandix-modal' }, [ E('div', { 'class': 'bandix-modal-body', }, [ @@ -3211,6 +3322,46 @@ return view.extend({ document.body.appendChild(bandixModal); + // 创建独立的流量配额模态框 + var trafficQuotaModal = E('div', { 'class': 'bandix-modal-overlay', 'id': 'traffic-quota-bandix-modal' }, [ + E('div', { 'class': 'modal-content bandix-modal' }, [ + E('div', { 'class': 'bandix-modal-header' }, [ + E('h3', { 'class': 'bandix-modal-title' }, _('Traffic Quota')) + ]), + E('div', { 'class': 'bandix-modal-body' }, [ + E('div', { 'class': 'device-summary', 'id': 'traffic-quota-device-summary' }), + E('div', { 'class': 'traffic-quota-section', 'style': 'margin-top: 0; padding-top: 0; border-top: none;' }, [ + E('div', { 'class': 'traffic-quota-header' }, [ + E('div', { 'style': 'font-weight: 600;' }, _('Quota Status')), + E('span', { 'class': 'traffic-quota-status', 'id': 'traffic-quota-status' }, _('Not configured')) + ]), + E('div', { 'style': 'font-size: 0.75rem; opacity: 0.7;' }, + _('WAN upload and download are counted together. Enter 0 for unlimited.')), + E('div', { 'class': 'traffic-quota-grid' }, [ + createQuotaInput('minute', _('Minute Quota')), + createQuotaInput('hourly', _('Hourly Quota')), + createQuotaInput('daily', _('Daily Quota')), + createQuotaInput('weekly', _('Weekly Quota')), + createQuotaInput('monthly', _('Monthly Quota')), + createQuotaInput('total', _('Lifetime Quota')) + ]), + E('div', { 'class': 'traffic-quota-usage', 'id': 'traffic-quota-usage' }, [ + E('div', { 'style': 'opacity: 0.7;' }, _('No quota usage data')) + ]), + E('div', { 'style': 'display: flex; justify-content: flex-end; gap: 8px; margin-top: 12px;' }, [ + E('button', { 'class': 'cbi-button cbi-button-negative', 'id': 'traffic-quota-delete-btn', 'style': 'display: none;' }, _('Delete Quota')), + E('button', { 'class': 'cbi-button cbi-button-positive', 'id': 'traffic-quota-save-btn' }, _('Save Quota')) + ]) + ]) + ]), + E('div', { 'class': 'bandix-modal-footer' }, [ + E('button', { 'class': 'cbi-button cbi-button-reset', 'id': 'traffic-quota-modal-close' }, _('Close')) + ]) + ]) + ]); + + document.body.appendChild(trafficQuotaModal); + // 创建添加规则模态框 var addRuleModal = E('div', { 'class': 'bandix-modal-overlay', 'id': 'add-rule-bandix-modal' }, [ E('div', { 'class': 'modal-content bandix-modal' }, [ @@ -3946,9 +4097,17 @@ return view.extend({ // 模态框事件处理 var currentDevice = null; + var currentQuotaDevice = null; + var quotaFormDirty = false; var editingRule = null; var showRateLimitModal; + trafficQuotaModal.querySelectorAll('.traffic-quota-input-row input, .traffic-quota-input-row select').forEach(function (input) { + input.addEventListener(input.tagName === 'SELECT' ? 'change' : 'input', function () { + quotaFormDirty = true; + }); + }); + // 显示模态框 showRateLimitModal = function (device) { currentDevice = device; @@ -3994,6 +4153,33 @@ return view.extend({ }, 300); } + function showTrafficQuotaModal(device) { + currentQuotaDevice = device; + quotaFormDirty = false; + var modal = document.getElementById('traffic-quota-bandix-modal'); + var deviceSummary = document.getElementById('traffic-quota-device-summary'); + var modalContent = [ + E('div', { 'class': 'device-summary-name' }, device.host || device.ip4), + E('div', { 'class': 'device-summary-details' }, device.ip4 + ' (' + device.mac + ')') + ]; + var modalBadges = buildDeviceUplinkChBadges(device); + if (modalBadges.length) { + modalContent.push(E('div', { 'class': 'device-summary-badges device-uplink-badges-wrap' }, modalBadges)); + } + deviceSummary.innerHTML = E('div', {}, modalContent).innerHTML; + renderTrafficQuotaForm(getTrafficQuotaForDevice(device.mac), false); + modal.classList.add('show'); + loadTrafficQuotaForm(); + } + + function hideTrafficQuotaModal() { + document.getElementById('traffic-quota-bandix-modal').classList.remove('show'); + setTimeout(function () { + currentQuotaDevice = null; + quotaFormDirty = false; + }, 300); + } + // 加载定时限速规则列表 function loadScheduleRules() { if (!currentDevice) return; @@ -4163,8 +4349,60 @@ return view.extend({ // 绑定 hostname 保存按钮事件 document.getElementById('hostname-save-btn').addEventListener('click', saveHostname); + document.getElementById('traffic-quota-save-btn').addEventListener('click', function () { + if (!currentQuotaDevice) return; + var mac = currentQuotaDevice.mac; + var values = ['minute', 'hourly', 'daily', 'weekly', 'monthly', 'total'].map(readQuotaInput); + if (values.some(function (value) { return value === null; })) { + ui.addNotification(null, E('p', {}, _('Quota values must be non-negative and within the supported range.')), 'error'); + return; + } + var button = this; + button.disabled = true; + setTrafficQuotaFormDisabled(true); + callSetTrafficQuota(mac, values[0], values[1], values[2], values[3], values[4], values[5]).then(function (result) { + if (result && result.success === false) throw new Error(result.error || _('Failed to save traffic quota')); + return fetchAllTrafficQuotas(true); + }).then(function () { + if (currentQuotaDevice && currentQuotaDevice.mac === mac) { + quotaFormDirty = false; + renderTrafficQuotaForm(getTrafficQuotaForDevice(mac), false); + } + updateDeviceData(); + }).catch(function (error) { + ui.addNotification(null, E('p', {}, _('Failed to save traffic quota') + ': ' + (error.message || error)), 'error'); + }).finally(function () { + button.disabled = false; + setTrafficQuotaFormDisabled(false); + }); + }); + + document.getElementById('traffic-quota-delete-btn').addEventListener('click', function () { + if (!currentQuotaDevice) return; + var mac = currentQuotaDevice.mac; + showConfirmDialog( + _('Delete Traffic Quota'), + _('Delete this quota and all of its accumulated usage?'), + function () { + callDeleteTrafficQuota(mac).then(function (result) { + if (result && result.success === false) throw new Error(result.error || _('Failed to delete traffic quota')); + return fetchAllTrafficQuotas(true); + }).then(function () { + if (currentQuotaDevice && currentQuotaDevice.mac === mac) { + quotaFormDirty = false; + renderTrafficQuotaForm(null, false); + } + updateDeviceData(); + }).catch(function (error) { + ui.addNotification(null, E('p', {}, _('Failed to delete traffic quota') + ': ' + (error.message || error)), 'error'); + }); + } + ); + }); + // 绑定关闭按钮事件 document.getElementById('bandix-modal-close').addEventListener('click', hideRateLimitModal); + document.getElementById('traffic-quota-modal-close').addEventListener('click', hideTrafficQuotaModal); // 历史趋势:状态与工具 var latestDevices = []; @@ -4175,6 +4413,154 @@ return view.extend({ var allScheduleRules = []; // 存储所有设备的定时限速规则 var isScheduleRulesLoading = false; // 防止轮询重入 + var allTrafficQuotas = []; + var trafficQuotasRequest = null; + var trafficQuotasLoadError = null; + + function fetchAllTrafficQuotas(forceRefresh) { + if (trafficQuotasRequest) { + if (!forceRefresh) return trafficQuotasRequest; + return trafficQuotasRequest.then(function () { + return fetchAllTrafficQuotas(true); + }); + } + trafficQuotasRequest = callGetTrafficQuotas().then(function (res) { + if (!res || res.success === false || res.status === 'error') { + throw new Error((res && (res.error || res.message)) || _('Failed to load traffic quotas')); + } + var quotas = []; + if (res && res.data && Array.isArray(res.data.quotas)) { + quotas = res.data.quotas; + } else if (res && Array.isArray(res.quotas)) { + quotas = res.quotas; + } + allTrafficQuotas = quotas; + trafficQuotasLoadError = null; + return quotas; + }).catch(function (error) { + console.error('Failed to fetch traffic quotas:', error); + allTrafficQuotas = []; + trafficQuotasLoadError = error && error.message ? error.message : _('Failed to load traffic quotas'); + return []; + }).finally(function () { + trafficQuotasRequest = null; + }); + return trafficQuotasRequest; + } + + function getTrafficQuotaForDevice(mac) { + for (var i = 0; i < allTrafficQuotas.length; i++) { + if (allTrafficQuotas[i] && allTrafficQuotas[i].mac === mac) { + return allTrafficQuotas[i]; + } + } + return null; + } + + function quotaPeriods(quota) { + return [ + { key: 'minute', label: _('Minute'), limit: quota.minute_bytes || 0, used: quota.minute_used_bytes || 0 }, + { key: 'hourly', label: _('Hourly'), limit: quota.hourly_bytes || 0, used: quota.hourly_used_bytes || 0 }, + { key: 'daily', label: _('Daily'), limit: quota.daily_bytes || 0, used: quota.daily_used_bytes || 0 }, + { key: 'weekly', label: _('Weekly'), limit: quota.weekly_bytes || 0, used: quota.weekly_used_bytes || 0 }, + { key: 'monthly', label: _('Monthly'), limit: quota.monthly_bytes || 0, used: quota.monthly_used_bytes || 0 }, + { key: 'total', label: _('Lifetime'), limit: quota.total_bytes || 0, used: quota.total_used_bytes || 0 } + ]; + } + + function setQuotaInput(period, bytes) { + var valueInput = document.getElementById('traffic-quota-' + period + '-value'); + var unitSelect = document.getElementById('traffic-quota-' + period + '-unit'); + if (!valueInput || !unitSelect) return; + var units = [1099511627776, 1073741824, 1048576, 1024, 1]; + var unit = period === 'minute' ? 1048576 : 1073741824; + if (bytes > 0) { + for (var i = 0; i < units.length; i++) { + if (bytes >= units[i] && bytes % units[i] === 0) { + unit = units[i]; + break; + } + } + } + unitSelect.value = String(unit); + valueInput.value = bytes > 0 ? String(bytes / unit) : '0'; + } + + function readQuotaInput(period) { + var value = Number(document.getElementById('traffic-quota-' + period + '-value').value || 0); + var unit = Number(document.getElementById('traffic-quota-' + period + '-unit').value || 1); + var bytes = Math.round(value * unit); + if (!Number.isFinite(bytes) || bytes < 0 || !Number.isSafeInteger(bytes)) return null; + return bytes; + } + + function setTrafficQuotaFormDisabled(disabled) { + trafficQuotaModal.querySelectorAll('.traffic-quota-input-row input, .traffic-quota-input-row select').forEach(function (input) { + input.disabled = disabled; + }); + } + + function renderTrafficQuotaForm(quota, preserveInputs) { + if (!preserveInputs) { + var periods = ['minute', 'hourly', 'daily', 'weekly', 'monthly', 'total']; + periods.forEach(function (period) { + setQuotaInput(period, quota ? (quota[period + '_bytes'] || 0) : 0); + }); + } + + var status = document.getElementById('traffic-quota-status'); + var usage = document.getElementById('traffic-quota-usage'); + var deleteBtn = document.getElementById('traffic-quota-delete-btn'); + if (!status || !usage || !deleteBtn) return; + + status.classList.toggle('blocked', !!(quota && quota.blocked)); + status.classList.toggle('unconfigured', !quota); + status.textContent = !quota ? (trafficQuotasLoadError ? _('Unavailable') : _('Not configured')) : (quota.blocked ? _('Blocked') : _('Active')); + deleteBtn.style.display = quota ? '' : 'none'; + usage.innerHTML = ''; + if (!quota) { + usage.appendChild(E('div', { 'style': 'opacity: 0.7;' }, trafficQuotasLoadError || _('Usage starts when a quota is first saved.'))); + return; + } + quotaPeriods(quota).forEach(function (period) { + usage.appendChild(E('div', { 'class': 'traffic-quota-usage-row' }, [ + E('span', {}, period.label), + E('span', {}, formatSize(period.used) + ' / ' + (period.limit > 0 ? formatSize(period.limit) : _('Unlimited'))) + ])); + }); + } + + function loadTrafficQuotaForm() { + if (!currentQuotaDevice) return Promise.resolve(); + var mac = currentQuotaDevice.mac; + renderTrafficQuotaForm(getTrafficQuotaForDevice(mac), quotaFormDirty); + return fetchAllTrafficQuotas().then(function () { + if (currentQuotaDevice && currentQuotaDevice.mac === mac) { + renderTrafficQuotaForm(getTrafficQuotaForDevice(mac), quotaFormDirty); + } + }); + } + + function buildTrafficQuotaCell(mac) { + var quota = getTrafficQuotaForDevice(mac); + if (!quota) return null; + var periods = quotaPeriods(quota).filter(function (period) { return period.limit > 0; }); + var mostUsed = null; + periods.forEach(function (period) { + var ratio = period.used / period.limit; + if (!mostUsed || ratio > mostUsed.ratio) mostUsed = { period: period, ratio: ratio }; + }); + var text = quota.blocked ? _('Quota exceeded') : _('Quota active'); + if (mostUsed) { + text = mostUsed.period.label + ': ' + formatSize(mostUsed.period.used) + ' / ' + formatSize(mostUsed.period.limit); + } + return E('div', { + 'class': 'traffic-quota-cell', + 'style': quota.blocked ? 'color: #ef4444; font-weight: 600;' : '', + 'title': quota.blocked ? _('WAN traffic is blocked because a quota was reached.') : _('Traffic Quota') + }, text); + } + // 获取所有定时限速规则 function fetchAllScheduleRules() { if (isScheduleRulesLoading) return Promise.resolve(); @@ -5862,7 +6248,7 @@ return view.extend({ createSortableHeader(_('Device Info'), 'online'), createSplitHeader(_('LAN Traffic'), 'lan_speed', 'lan_traffic'), createSplitHeader(_('WAN Traffic'), 'wan_speed', 'wan_traffic'), - E('th', {}, _('Schedule Rules')), + E('th', {}, _('Rules / Quota')), E('th', {}, _('Actions')) ]) ]), @@ -5890,9 +6276,9 @@ return view.extend({ // 填充数据 filteredDevices.forEach(function (device) { var isOnline = isDeviceOnline(device); + var deviceQuota = getTrafficQuotaForDevice(device.mac); - // 默认使用窄主题样式 - var buttonText = '⚙'; + var buttonText = 'R'; var actionButton = E('button', { 'class': 'cbi-button cbi-button-action', @@ -5903,6 +6289,15 @@ return view.extend({ showRateLimitModal(device); }); + var quotaButton = E('button', { + 'class': 'cbi-button cbi-button-neutral', + 'title': _('Traffic Quota') + }, 'Q'); + + quotaButton.addEventListener('click', function () { + showTrafficQuotaModal(device); + }); + var deleteButton = E('button', { 'class': 'cbi-button cbi-button-reset', 'title': _('Delete Device') @@ -5935,7 +6330,10 @@ return view.extend({ E('span', { 'class': 'device-status ' + (isOnline ? 'online' : 'offline') }), - device.host || '-' + device.host || '-', + deviceQuota && deviceQuota.blocked + ? E('span', { 'class': 'traffic-quota-blocked-badge' }, _('Quota blocked')) + : '' ]), E('div', { 'class': 'device-ip' }, [ device.conn ? E('span', { @@ -6149,12 +6547,14 @@ return view.extend({ }; } - return E('td', {}, rulesInfo); + var quotaCell = buildTrafficQuotaCell(device.mac); + return E('td', {}, quotaCell ? [rulesInfo, quotaCell] : rulesInfo); })(), // 操作 E('td', { 'class': 'device-actions' }, [ actionButton, + quotaButton, deleteButton ]) ]); @@ -6193,6 +6593,16 @@ return view.extend({ }); return cardActionBtn; })(), + (function () { + var cardQuotaBtn = E('button', { + 'class': 'cbi-button cbi-button-neutral', + 'title': _('Traffic Quota') + }, 'Q'); + cardQuotaBtn.addEventListener('click', function () { + showTrafficQuotaModal(device); + }); + return cardQuotaBtn; + })(), (function () { var cardDeleteBtn = E('button', { 'class': 'cbi-button cbi-button-reset', @@ -6279,6 +6689,14 @@ return view.extend({ rulesContent ]); })(), + (function () { + var quotaCell = buildTrafficQuotaCell(device.mac); + if (!quotaCell) return ''; + return E('div', { 'class': 'device-card-section' }, [ + E('div', { 'class': 'device-card-section-label' }, _('Traffic Quota')), + quotaCell + ]); + })(), // 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')), @@ -7499,12 +7917,25 @@ return view.extend({ }); }, 5000); + // 轮询获取流量配额状态(每1秒) + poll.add(function () { + return fetchAllTrafficQuotas().then(function () { + if (window.__bandixRenderTable) window.__bandixRenderTable(); + if (currentQuotaDevice) { + renderTrafficQuotaForm(getTrafficQuotaForDevice(currentQuotaDevice.mac), quotaFormDirty); + } + }); + }, 1); + // Traffic Statistics 不再自动刷新,改为手动查询 // 立即执行一次,不等待轮询 updateDeviceData(); refreshWhitelistStatus(); fetchAllScheduleRules(); + fetchAllTrafficQuotas().then(function () { + if (window.__bandixRenderTable) window.__bandixRenderTable(); + }); // 初始化时间范围查询功能 setTimeout(function () { diff --git a/luci-app-bandix/po/es/bandix.po b/luci-app-bandix/po/es/bandix.po index 9a5e5885..053d0a08 100644 --- a/luci-app-bandix/po/es/bandix.po +++ b/luci-app-bandix/po/es/bandix.po @@ -1258,3 +1258,93 @@ msgstr "ID de programa ancla de egreso TCX" msgid "Used when tc_order is before/after. Must be a valid egress program id on the same interface." msgstr "Se usa cuando tc_order es before/after. Debe ser un ID de programa de egreso valido en la misma interfaz." + +msgid "Traffic Quota" +msgstr "Cuota de tráfico" + +msgid "Quota Status" +msgstr "Estado de la cuota" + +msgid "Minute Quota" +msgstr "Cuota por minuto" + +msgid "Hourly Quota" +msgstr "Cuota por hora" + +msgid "Daily Quota" +msgstr "Cuota diaria" + +msgid "Weekly Quota" +msgstr "Cuota semanal" + +msgid "Monthly Quota" +msgstr "Cuota mensual" + +msgid "Lifetime Quota" +msgstr "Cuota total acumulada" + +msgid "WAN upload and download are counted together. Enter 0 for unlimited." +msgstr "La subida y la descarga WAN se contabilizan juntas. Introduzca 0 para no establecer límite." + +msgid "Not configured" +msgstr "Sin configurar" + +msgid "Blocked" +msgstr "Bloqueado" + +msgid "Active" +msgstr "Activa" + +msgid "No quota usage data" +msgstr "No hay datos de uso de la cuota" + +msgid "Delete Quota" +msgstr "Eliminar cuota" + +msgid "Save Quota" +msgstr "Guardar cuota" + +msgid "Usage starts when a quota is first saved." +msgstr "El uso comienza a contabilizarse al guardar una cuota por primera vez." + +msgid "Minute" +msgstr "Minuto" + +msgid "Lifetime" +msgstr "Total acumulado" + +msgid "Quota exceeded" +msgstr "Cuota excedida" + +msgid "Quota active" +msgstr "Cuota activa" + +msgid "WAN traffic is blocked because a quota was reached." +msgstr "El tráfico WAN está bloqueado porque se alcanzó una cuota." + +msgid "Quota blocked" +msgstr "Bloqueado por cuota" + +msgid "Quota values must be non-negative and within the supported range." +msgstr "Los valores de cuota deben ser no negativos y estar dentro del rango admitido." + +msgid "Failed to save traffic quota" +msgstr "No se pudo guardar la cuota de tráfico" + +msgid "Delete Traffic Quota" +msgstr "Eliminar cuota de tráfico" + +msgid "Delete this quota and all of its accumulated usage?" +msgstr "¿Eliminar esta cuota y todo su uso acumulado?" + +msgid "Failed to delete traffic quota" +msgstr "No se pudo eliminar la cuota de tráfico" + +msgid "Rules / Quota" +msgstr "Reglas / Cuota" + +msgid "Failed to load traffic quotas" +msgstr "No se pudieron cargar las cuotas de tráfico" + +msgid "Unavailable" +msgstr "No disponible" diff --git a/luci-app-bandix/po/fr/bandix.po b/luci-app-bandix/po/fr/bandix.po index f4dd364c..ca9b8b20 100644 --- a/luci-app-bandix/po/fr/bandix.po +++ b/luci-app-bandix/po/fr/bandix.po @@ -1258,3 +1258,93 @@ msgstr "ID du programme d ancrage egress TCX" msgid "Used when tc_order is before/after. Must be a valid egress program id on the same interface." msgstr "Utilise lorsque tc_order est before/after. Doit etre un ID de programme egress valide sur la meme interface." + +msgid "Traffic Quota" +msgstr "Quota de trafic" + +msgid "Quota Status" +msgstr "État du quota" + +msgid "Minute Quota" +msgstr "Quota par minute" + +msgid "Hourly Quota" +msgstr "Quota par heure" + +msgid "Daily Quota" +msgstr "Quota journalier" + +msgid "Weekly Quota" +msgstr "Quota hebdomadaire" + +msgid "Monthly Quota" +msgstr "Quota mensuel" + +msgid "Lifetime Quota" +msgstr "Quota total cumulé" + +msgid "WAN upload and download are counted together. Enter 0 for unlimited." +msgstr "Le trafic montant et descendant WAN est comptabilisé ensemble. Saisissez 0 pour un trafic illimité." + +msgid "Not configured" +msgstr "Non configuré" + +msgid "Blocked" +msgstr "Bloqué" + +msgid "Active" +msgstr "Actif" + +msgid "No quota usage data" +msgstr "Aucune donnée d'utilisation du quota" + +msgid "Delete Quota" +msgstr "Supprimer le quota" + +msgid "Save Quota" +msgstr "Enregistrer le quota" + +msgid "Usage starts when a quota is first saved." +msgstr "Le suivi de l'utilisation commence lors du premier enregistrement d'un quota." + +msgid "Minute" +msgstr "Minute" + +msgid "Lifetime" +msgstr "Total cumulé" + +msgid "Quota exceeded" +msgstr "Quota dépassé" + +msgid "Quota active" +msgstr "Quota actif" + +msgid "WAN traffic is blocked because a quota was reached." +msgstr "Le trafic WAN est bloqué car un quota a été atteint." + +msgid "Quota blocked" +msgstr "Blocage par quota" + +msgid "Quota values must be non-negative and within the supported range." +msgstr "Les valeurs de quota doivent être positives ou nulles et respecter la plage prise en charge." + +msgid "Failed to save traffic quota" +msgstr "Impossible d'enregistrer le quota de trafic" + +msgid "Delete Traffic Quota" +msgstr "Supprimer le quota de trafic" + +msgid "Delete this quota and all of its accumulated usage?" +msgstr "Supprimer ce quota et toutes ses données d'utilisation cumulées ?" + +msgid "Failed to delete traffic quota" +msgstr "Impossible de supprimer le quota de trafic" + +msgid "Rules / Quota" +msgstr "Règles / Quota" + +msgid "Failed to load traffic quotas" +msgstr "Impossible de charger les quotas de trafic" + +msgid "Unavailable" +msgstr "Indisponible" diff --git a/luci-app-bandix/po/ja/bandix.po b/luci-app-bandix/po/ja/bandix.po index 2387a50e..63c82c52 100644 --- a/luci-app-bandix/po/ja/bandix.po +++ b/luci-app-bandix/po/ja/bandix.po @@ -1258,3 +1258,93 @@ msgstr "TCX egressアンカープログラムID" msgid "Used when tc_order is before/after. Must be a valid egress program id on the same interface." msgstr "tc_orderがbefore/afterのときに使用します。同じインターフェース上の有効なegressプログラムIDである必要があります。" + +msgid "Traffic Quota" +msgstr "トラフィッククォータ" + +msgid "Quota Status" +msgstr "クォータの状態" + +msgid "Minute Quota" +msgstr "1分あたりのクォータ" + +msgid "Hourly Quota" +msgstr "1時間あたりのクォータ" + +msgid "Daily Quota" +msgstr "1日あたりのクォータ" + +msgid "Weekly Quota" +msgstr "1週間あたりのクォータ" + +msgid "Monthly Quota" +msgstr "1か月あたりのクォータ" + +msgid "Lifetime Quota" +msgstr "累計クォータ" + +msgid "WAN upload and download are counted together. Enter 0 for unlimited." +msgstr "WANのアップロードとダウンロードは合算されます。無制限にするには0を入力してください。" + +msgid "Not configured" +msgstr "未設定" + +msgid "Blocked" +msgstr "ブロック中" + +msgid "Active" +msgstr "有効" + +msgid "No quota usage data" +msgstr "クォータ使用量データはありません" + +msgid "Delete Quota" +msgstr "クォータを削除" + +msgid "Save Quota" +msgstr "クォータを保存" + +msgid "Usage starts when a quota is first saved." +msgstr "クォータを初めて保存した時点から使用量の計測を開始します。" + +msgid "Minute" +msgstr "1分" + +msgid "Lifetime" +msgstr "累計" + +msgid "Quota exceeded" +msgstr "クォータ超過" + +msgid "Quota active" +msgstr "クォータ有効" + +msgid "WAN traffic is blocked because a quota was reached." +msgstr "クォータに達したためWANトラフィックがブロックされています。" + +msgid "Quota blocked" +msgstr "クォータによりブロック" + +msgid "Quota values must be non-negative and within the supported range." +msgstr "クォータ値は0以上で、サポートされている範囲内である必要があります。" + +msgid "Failed to save traffic quota" +msgstr "トラフィッククォータを保存できませんでした" + +msgid "Delete Traffic Quota" +msgstr "トラフィッククォータを削除" + +msgid "Delete this quota and all of its accumulated usage?" +msgstr "このクォータと累積された使用量をすべて削除しますか?" + +msgid "Failed to delete traffic quota" +msgstr "トラフィッククォータを削除できませんでした" + +msgid "Rules / Quota" +msgstr "ルール / クォータ" + +msgid "Failed to load traffic quotas" +msgstr "トラフィッククォータを読み込めませんでした" + +msgid "Unavailable" +msgstr "利用不可" diff --git a/luci-app-bandix/po/pl/bandix.po b/luci-app-bandix/po/pl/bandix.po index 60f92426..2a354ea0 100644 --- a/luci-app-bandix/po/pl/bandix.po +++ b/luci-app-bandix/po/pl/bandix.po @@ -1258,3 +1258,93 @@ msgstr "ID programu kotwicy TCX egress" msgid "Used when tc_order is before/after. Must be a valid egress program id on the same interface." msgstr "Uzywane, gdy tc_order to before/after. Musi to byc prawidlowe ID programu egress na tym samym interfejsie." + +msgid "Traffic Quota" +msgstr "Limit transferu" + +msgid "Quota Status" +msgstr "Stan limitu" + +msgid "Minute Quota" +msgstr "Limit minutowy" + +msgid "Hourly Quota" +msgstr "Limit godzinowy" + +msgid "Daily Quota" +msgstr "Limit dzienny" + +msgid "Weekly Quota" +msgstr "Limit tygodniowy" + +msgid "Monthly Quota" +msgstr "Limit miesięczny" + +msgid "Lifetime Quota" +msgstr "Łączny limit" + +msgid "WAN upload and download are counted together. Enter 0 for unlimited." +msgstr "Ruch wysyłany i pobierany przez WAN jest sumowany. Wpisz 0, aby wyłączyć limit." + +msgid "Not configured" +msgstr "Nie skonfigurowano" + +msgid "Blocked" +msgstr "Zablokowano" + +msgid "Active" +msgstr "Aktywny" + +msgid "No quota usage data" +msgstr "Brak danych o wykorzystaniu limitu" + +msgid "Delete Quota" +msgstr "Usuń limit" + +msgid "Save Quota" +msgstr "Zapisz limit" + +msgid "Usage starts when a quota is first saved." +msgstr "Naliczanie użycia rozpoczyna się po pierwszym zapisaniu limitu." + +msgid "Minute" +msgstr "Minuta" + +msgid "Lifetime" +msgstr "Łącznie" + +msgid "Quota exceeded" +msgstr "Limit przekroczony" + +msgid "Quota active" +msgstr "Limit aktywny" + +msgid "WAN traffic is blocked because a quota was reached." +msgstr "Ruch WAN jest zablokowany, ponieważ osiągnięto limit." + +msgid "Quota blocked" +msgstr "Blokada przez limit" + +msgid "Quota values must be non-negative and within the supported range." +msgstr "Wartości limitu muszą być nieujemne i mieścić się w obsługiwanym zakresie." + +msgid "Failed to save traffic quota" +msgstr "Nie udało się zapisać limitu transferu" + +msgid "Delete Traffic Quota" +msgstr "Usuń limit transferu" + +msgid "Delete this quota and all of its accumulated usage?" +msgstr "Usunąć ten limit i wszystkie dane o naliczonym użyciu?" + +msgid "Failed to delete traffic quota" +msgstr "Nie udało się usunąć limitu transferu" + +msgid "Rules / Quota" +msgstr "Reguły / Limit" + +msgid "Failed to load traffic quotas" +msgstr "Nie udało się wczytać limitów transferu" + +msgid "Unavailable" +msgstr "Niedostępne" diff --git a/luci-app-bandix/po/ru/bandix.po b/luci-app-bandix/po/ru/bandix.po index 67d5b403..27cf3edb 100644 --- a/luci-app-bandix/po/ru/bandix.po +++ b/luci-app-bandix/po/ru/bandix.po @@ -1258,3 +1258,93 @@ msgstr "ID якорной программы TCX egress" msgid "Used when tc_order is before/after. Must be a valid egress program id on the same interface." msgstr "Используется, когда tc_order имеет значение before/after. Должен быть корректный ID egress-программы на том же интерфейсе." + +msgid "Traffic Quota" +msgstr "Квота трафика" + +msgid "Quota Status" +msgstr "Состояние квоты" + +msgid "Minute Quota" +msgstr "Минутная квота" + +msgid "Hourly Quota" +msgstr "Часовая квота" + +msgid "Daily Quota" +msgstr "Суточная квота" + +msgid "Weekly Quota" +msgstr "Недельная квота" + +msgid "Monthly Quota" +msgstr "Месячная квота" + +msgid "Lifetime Quota" +msgstr "Общая квота" + +msgid "WAN upload and download are counted together. Enter 0 for unlimited." +msgstr "Входящий и исходящий трафик WAN учитываются вместе. Введите 0, чтобы отключить ограничение." + +msgid "Not configured" +msgstr "Не настроено" + +msgid "Blocked" +msgstr "Заблокировано" + +msgid "Active" +msgstr "Активна" + +msgid "No quota usage data" +msgstr "Нет данных об использовании квоты" + +msgid "Delete Quota" +msgstr "Удалить квоту" + +msgid "Save Quota" +msgstr "Сохранить квоту" + +msgid "Usage starts when a quota is first saved." +msgstr "Учет трафика начинается после первого сохранения квоты." + +msgid "Minute" +msgstr "Минута" + +msgid "Lifetime" +msgstr "Всего" + +msgid "Quota exceeded" +msgstr "Квота превышена" + +msgid "Quota active" +msgstr "Квота активна" + +msgid "WAN traffic is blocked because a quota was reached." +msgstr "Трафик WAN заблокирован, так как достигнута квота." + +msgid "Quota blocked" +msgstr "Блокировка по квоте" + +msgid "Quota values must be non-negative and within the supported range." +msgstr "Значения квоты должны быть неотрицательными и находиться в поддерживаемом диапазоне." + +msgid "Failed to save traffic quota" +msgstr "Не удалось сохранить квоту трафика" + +msgid "Delete Traffic Quota" +msgstr "Удалить квоту трафика" + +msgid "Delete this quota and all of its accumulated usage?" +msgstr "Удалить эту квоту и все накопленные данные об использовании?" + +msgid "Failed to delete traffic quota" +msgstr "Не удалось удалить квоту трафика" + +msgid "Rules / Quota" +msgstr "Правила / Квота" + +msgid "Failed to load traffic quotas" +msgstr "Не удалось загрузить квоты трафика" + +msgid "Unavailable" +msgstr "Недоступно" diff --git a/luci-app-bandix/po/zh_Hans/bandix.po b/luci-app-bandix/po/zh_Hans/bandix.po index 8307d54a..0e09a773 100644 --- a/luci-app-bandix/po/zh_Hans/bandix.po +++ b/luci-app-bandix/po/zh_Hans/bandix.po @@ -1285,3 +1285,93 @@ msgstr "TCX 出站锚点程序 ID" msgid "Used when tc_order is before/after. Must be a valid egress program id on the same interface." msgstr "当 tc_order 为 before/after 时使用。必须是同一接口上的有效出站程序 ID。" + +msgid "Traffic Quota" +msgstr "流量配额" + +msgid "Quota Status" +msgstr "配额状态" + +msgid "Hourly Quota" +msgstr "每小时配额" + +msgid "Daily Quota" +msgstr "每天配额" + +msgid "Weekly Quota" +msgstr "每周配额" + +msgid "Monthly Quota" +msgstr "每月配额" + +msgid "Lifetime Quota" +msgstr "累计总量配额" + +msgid "WAN upload and download are counted together. Enter 0 for unlimited." +msgstr "WAN 上传和下载合并计算;输入 0 表示不限量。" + +msgid "Not configured" +msgstr "未配置" + +msgid "Blocked" +msgstr "已阻断" + +msgid "Active" +msgstr "生效中" + +msgid "No quota usage data" +msgstr "暂无配额用量数据" + +msgid "Delete Quota" +msgstr "删除配额" + +msgid "Save Quota" +msgstr "保存配额" + +msgid "Usage starts when a quota is first saved." +msgstr "首次保存配额后开始累计用量。" + +msgid "Lifetime" +msgstr "累计" + +msgid "Quota exceeded" +msgstr "配额已用尽" + +msgid "Quota active" +msgstr "配额生效中" + +msgid "WAN traffic is blocked because a quota was reached." +msgstr "因达到流量配额,WAN 流量已被阻断。" + +msgid "Quota blocked" +msgstr "配额阻断" + +msgid "Quota values must be non-negative and within the supported range." +msgstr "配额必须是支持范围内的非负数。" + +msgid "Failed to save traffic quota" +msgstr "保存流量配额失败" + +msgid "Delete Traffic Quota" +msgstr "删除流量配额" + +msgid "Delete this quota and all of its accumulated usage?" +msgstr "确定删除此配额及其全部累计用量吗?" + +msgid "Failed to delete traffic quota" +msgstr "删除流量配额失败" + +msgid "Rules / Quota" +msgstr "规则 / 配额" + +msgid "Failed to load traffic quotas" +msgstr "加载流量配额失败" + +msgid "Unavailable" +msgstr "不可用" + +msgid "Minute Quota" +msgstr "每分钟配额" + +msgid "Minute" +msgstr "每分钟" diff --git a/luci-app-bandix/po/zh_Hant/bandix.po b/luci-app-bandix/po/zh_Hant/bandix.po index 1d6a37d2..40d6f129 100644 --- a/luci-app-bandix/po/zh_Hant/bandix.po +++ b/luci-app-bandix/po/zh_Hant/bandix.po @@ -1258,3 +1258,93 @@ msgstr "TCX 出站錨點程式 ID" msgid "Used when tc_order is before/after. Must be a valid egress program id on the same interface." msgstr "當 tc_order 為 before/after 時使用。必須是同一介面上的有效出站程式 ID。" + +msgid "Traffic Quota" +msgstr "流量配額" + +msgid "Quota Status" +msgstr "配額狀態" + +msgid "Minute Quota" +msgstr "每分鐘配額" + +msgid "Hourly Quota" +msgstr "每小時配額" + +msgid "Daily Quota" +msgstr "每天配額" + +msgid "Weekly Quota" +msgstr "每週配額" + +msgid "Monthly Quota" +msgstr "每月配額" + +msgid "Lifetime Quota" +msgstr "累計總量配額" + +msgid "WAN upload and download are counted together. Enter 0 for unlimited." +msgstr "WAN 上傳和下載合併計算;輸入 0 表示不限量。" + +msgid "Not configured" +msgstr "未設定" + +msgid "Blocked" +msgstr "已阻斷" + +msgid "Active" +msgstr "生效中" + +msgid "No quota usage data" +msgstr "暫無配額用量資料" + +msgid "Delete Quota" +msgstr "刪除配額" + +msgid "Save Quota" +msgstr "儲存配額" + +msgid "Usage starts when a quota is first saved." +msgstr "首次儲存配額後開始累計用量。" + +msgid "Minute" +msgstr "每分鐘" + +msgid "Lifetime" +msgstr "累計" + +msgid "Quota exceeded" +msgstr "配額已用盡" + +msgid "Quota active" +msgstr "配額生效中" + +msgid "WAN traffic is blocked because a quota was reached." +msgstr "因達到流量配額,WAN 流量已被阻斷。" + +msgid "Quota blocked" +msgstr "配額阻斷" + +msgid "Quota values must be non-negative and within the supported range." +msgstr "配額必須是支援範圍內的非負數。" + +msgid "Failed to save traffic quota" +msgstr "儲存流量配額失敗" + +msgid "Delete Traffic Quota" +msgstr "刪除流量配額" + +msgid "Delete this quota and all of its accumulated usage?" +msgstr "確定刪除此配額及其全部累計用量嗎?" + +msgid "Failed to delete traffic quota" +msgstr "刪除流量配額失敗" + +msgid "Rules / Quota" +msgstr "規則 / 配額" + +msgid "Failed to load traffic quotas" +msgstr "載入流量配額失敗" + +msgid "Unavailable" +msgstr "無法使用" diff --git a/luci-app-bandix/root/usr/libexec/rpcd/luci.bandix b/luci-app-bandix/root/usr/libexec/rpcd/luci.bandix index d4bdec52..f6a5677e 100755 --- a/luci-app-bandix/root/usr/libexec/rpcd/luci.bandix +++ b/luci-app-bandix/root/usr/libexec/rpcd/luci.bandix @@ -19,6 +19,7 @@ readonly BANDIX_TRAFFIC_USAGE_INCREMENTS_API="$BANDIX_API_BASE/api/traffic/usage readonly BANDIX_RATE_LIMIT_WHITELIST_API="$BANDIX_API_BASE/api/traffic/rate_limit/whitelist" readonly BANDIX_RATE_LIMIT_WHITELIST_ENABLED_API="$BANDIX_API_BASE/api/traffic/rate_limit/whitelist/enabled" readonly BANDIX_RATE_LIMIT_DEFAULT_API="$BANDIX_API_BASE/api/traffic/rate_limit/default" +readonly BANDIX_TRAFFIC_QUOTAS_API="$BANDIX_API_BASE/api/traffic/quotas" # 通用函数:创建简单的JSON响应 make_value() { @@ -134,6 +135,71 @@ set_default_rate_limit() { echo "$response" } +get_traffic_quotas() { + local response=$(curl -s --connect-timeout 2 --max-time 5 -X GET "$BANDIX_TRAFFIC_QUOTAS_API" 2>/dev/null) + if [ $? -ne 0 ] || [ -z "$response" ]; then + make_error "Failed to connect to Bandix service" + return + fi + echo "$response" +} + +set_traffic_quota() { + local mac="$1" + local minute_bytes="$2" + local hourly_bytes="$3" + local daily_bytes="$4" + local weekly_bytes="$5" + local monthly_bytes="$6" + local total_bytes="$7" + + if [ -z "$mac" ]; then + make_error "MAC address is required" + return + fi + for value in "$minute_bytes" "$hourly_bytes" "$daily_bytes" "$weekly_bytes" "$monthly_bytes" "$total_bytes"; do + case "$value" in + ''|*[!0-9]*) + make_error "Quota values must be non-negative integers" + return + ;; + esac + done + + local mac_escaped=$(escape_json_string "$mac") + local request_data="{ + \"mac\": \"$mac_escaped\", + \"minute_bytes\": $minute_bytes, + \"hourly_bytes\": $hourly_bytes, + \"daily_bytes\": $daily_bytes, + \"weekly_bytes\": $weekly_bytes, + \"monthly_bytes\": $monthly_bytes, + \"total_bytes\": $total_bytes + }" + local response=$(curl -s --connect-timeout 3 --max-time 10 -X POST -H "Content-Type: application/json" -d "$request_data" "$BANDIX_TRAFFIC_QUOTAS_API" 2>/dev/null) + if [ $? -eq 0 ] && [ -n "$response" ] && echo "$response" | grep -q '"status":[[:space:]]*"success"'; then + make_success "Traffic quota saved successfully" + else + make_error "Failed to save traffic quota" + fi +} + +delete_traffic_quota() { + local mac="$1" + if [ -z "$mac" ]; then + make_error "MAC address is required" + return + fi + local mac_escaped=$(escape_json_string "$mac") + local request_data="{\"mac\": \"$mac_escaped\"}" + local response=$(curl -s --connect-timeout 3 --max-time 10 -X DELETE -H "Content-Type: application/json" -d "$request_data" "$BANDIX_TRAFFIC_QUOTAS_API" 2>/dev/null) + if [ $? -eq 0 ] && [ -n "$response" ] && echo "$response" | grep -q '"status":[[:space:]]*"success"'; then + make_success "Traffic quota deleted successfully" + else + make_error "Failed to delete traffic quota" + fi +} + # 删除设备 delete_device() { local mac="$1" @@ -1348,6 +1414,23 @@ case "$1" in json_add_int "wan_rx_rate_limit" json_add_int "wan_tx_rate_limit" json_close_object + + json_add_object "getTrafficQuotas" + json_close_object + + json_add_object "setTrafficQuota" + json_add_string "mac" + json_add_int "minute_bytes" + json_add_int "hourly_bytes" + json_add_int "daily_bytes" + json_add_int "weekly_bytes" + json_add_int "monthly_bytes" + json_add_int "total_bytes" + json_close_object + + json_add_object "deleteTrafficQuota" + json_add_string "mac" + json_close_object json_dump json_cleanup @@ -1793,6 +1876,57 @@ case "$1" in fi set_default_rate_limit "$wan_rx_rate_limit" "$wan_tx_rate_limit" ;; + getTrafficQuotas) + get_traffic_quotas + ;; + setTrafficQuota) + mac="" + minute_bytes="" + hourly_bytes="" + daily_bytes="" + weekly_bytes="" + monthly_bytes="" + total_bytes="" + input="" + if read -t 1 -r input; then :; fi + if [ -n "$input" ]; then + mac="$(echo "$input" | jsonfilter -e '$[0]' 2>/dev/null)" + [ -z "$mac" ] && mac="$(echo "$input" | jsonfilter -e '$.mac' 2>/dev/null)" + minute_bytes="$(echo "$input" | jsonfilter -e '$[1]' 2>/dev/null)" + [ -z "$minute_bytes" ] && minute_bytes="$(echo "$input" | jsonfilter -e '$.minute_bytes' 2>/dev/null)" + hourly_bytes="$(echo "$input" | jsonfilter -e '$[2]' 2>/dev/null)" + [ -z "$hourly_bytes" ] && hourly_bytes="$(echo "$input" | jsonfilter -e '$.hourly_bytes' 2>/dev/null)" + daily_bytes="$(echo "$input" | jsonfilter -e '$[3]' 2>/dev/null)" + [ -z "$daily_bytes" ] && daily_bytes="$(echo "$input" | jsonfilter -e '$.daily_bytes' 2>/dev/null)" + weekly_bytes="$(echo "$input" | jsonfilter -e '$[4]' 2>/dev/null)" + [ -z "$weekly_bytes" ] && weekly_bytes="$(echo "$input" | jsonfilter -e '$.weekly_bytes' 2>/dev/null)" + monthly_bytes="$(echo "$input" | jsonfilter -e '$[5]' 2>/dev/null)" + [ -z "$monthly_bytes" ] && monthly_bytes="$(echo "$input" | jsonfilter -e '$.monthly_bytes' 2>/dev/null)" + total_bytes="$(echo "$input" | jsonfilter -e '$[6]' 2>/dev/null)" + [ -z "$total_bytes" ] && total_bytes="$(echo "$input" | jsonfilter -e '$.total_bytes' 2>/dev/null)" + else + mac="$3" + minute_bytes="$4" + hourly_bytes="$5" + daily_bytes="$6" + weekly_bytes="$7" + monthly_bytes="$8" + total_bytes="$9" + fi + set_traffic_quota "$mac" "$minute_bytes" "$hourly_bytes" "$daily_bytes" "$weekly_bytes" "$monthly_bytes" "$total_bytes" + ;; + deleteTrafficQuota) + mac="" + input="" + if read -t 1 -r input; then :; fi + if [ -n "$input" ]; then + mac="$(echo "$input" | jsonfilter -e '$[0]' 2>/dev/null)" + [ -z "$mac" ] && mac="$(echo "$input" | jsonfilter -e '$.mac' 2>/dev/null)" + else + mac="$3" + fi + delete_traffic_quota "$mac" + ;; 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 452ec7dd..7c8ea354 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 @@ -26,7 +26,8 @@ "installUpdate", "getTrafficUsageRanking", "getTrafficUsageIncrements", - "getRateLimitWhitelist" + "getRateLimitWhitelist", + "getTrafficQuotas" ] }, "uci": [ @@ -64,7 +65,10 @@ "addRateLimitWhitelist", "deleteRateLimitWhitelist", "setDefaultRateLimit", - "updateScheduleLimit" + "updateScheduleLimit", + "getTrafficQuotas", + "setTrafficQuota", + "deleteTrafficQuota" ] }, "uci": [ @@ -72,4 +76,4 @@ ] } } -} \ No newline at end of file +} diff --git a/luci-app-clashoo/Makefile b/luci-app-clashoo/Makefile index d6fbbe9f..52d19c8e 100644 --- a/luci-app-clashoo/Makefile +++ b/luci-app-clashoo/Makefile @@ -2,7 +2,7 @@ include $(TOPDIR)/rules.mk PKG_NAME:=luci-app-clashoo PKG_VERSION:=1.30.0 -PKG_RELEASE:=11 +PKG_RELEASE:=12 PKG_MAINTAINER:=kenzok8 PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME) @@ -75,7 +75,7 @@ define Package/luci-i18n-clashoo-zh-cn SECTION:=luci CATEGORY:=LuCI TITLE:=luci-app-clashoo - Simplified Chinese (zh-cn) - DEPENDS:=+luci-app-clashoo + DEPENDS:=luci-app-clashoo DEFAULT:=LUCI_LANG_zh_Hans||(ALL&&m) HIDDEN:=1 PKGARCH:=all diff --git a/luci-app-clashoo/root/usr/share/rpcd/ucode/luci.clashoo b/luci-app-clashoo/root/usr/share/rpcd/ucode/luci.clashoo index 5dbce3b3..ac4c500e 100755 --- a/luci-app-clashoo/root/usr/share/rpcd/ucode/luci.clashoo +++ b/luci-app-clashoo/root/usr/share/rpcd/ucode/luci.clashoo @@ -3361,9 +3361,9 @@ return { success: false, error: 'missing_yq', message: '缺少 yq,无法注入 if (!access(exploded, "r")) { system("cp -f " + shell_quote(tmp_tpl) + " " + shell_quote(exploded)); } - /* proxy-providers 和 rule-providers 两处都注入(兼容不同模板格式) */ + /* 只替换 proxy-providers.url,rule-providers 指向规则集保持原样 */ let inject_cmd = "URL=" + shell_quote(sub_url) - + " yq e '.[\"proxy-providers\"][].url = env(URL) | .[\"rule-providers\"][].url = env(URL)' " + + " yq e '.[\"proxy-providers\"][].url = env(URL)' " + shell_quote(exploded) + " > " + shell_quote(tmp_out); if (system(inject_cmd) != 0 || !access(tmp_out, "r")) { system("rm -f " + shell_quote(exploded)); diff --git a/openwrt-bandix/Makefile b/openwrt-bandix/Makefile index 42f1d465..3bb94196 100644 --- a/openwrt-bandix/Makefile +++ b/openwrt-bandix/Makefile @@ -1,7 +1,7 @@ include $(TOPDIR)/rules.mk PKG_NAME:=bandix -PKG_VERSION:=0.12.9 +PKG_VERSION:=0.12.10 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.12.9 +RUST_BANDIX_VERSION:=0.12.10 RUST_BINARY_FILENAME:=bandix-$(RUST_BANDIX_VERSION)-$(RUSTC_TARGET_ARCH).tar.gz @@ -23,7 +23,7 @@ PKG_SOURCE_URL:=https://github.com/timsaya/bandix/releases/download/v$(RUST_BAND # 修正二进制文件校验值 ifeq ($(ARCH),aarch64) - PKG_HASH:=17803eeb6964187626e6af2e2b0a3b081d87a8017ff2b6505ba313b0c7b8ba7f + PKG_HASH:=653e554a95ff1a336def82b9b4899466b6fc62a6e8fd42ff786eaedebfe0b911 else ifeq ($(ARCH),arm) # update.sh skip this tag, do not delete this comment # Ref https://github.com/openwrt/packages/blob/master/lang/rust/rust-values.mk @@ -39,28 +39,28 @@ else ifeq ($(ARCH),arm) # update.sh skip this tag, do not delete this comment endif ifeq ($(ARM_CPU_TYPE),arm-armel) - PKG_HASH:=cbf9cf705218211e0a5cd1427049632aa40086831feee2994e9d4b7b2cf46ab4 + PKG_HASH:=4b6c96715fbda3e2e60c41aa72286fb10da09997a05ed98311cf377c22848e65 else ifeq ($(ARM_CPU_TYPE),arm-armhf) - PKG_HASH:=2901c5f98e77fce574bcaef1aa8c20acd58ca341f3f3bd5f3bc80d745946f632 + PKG_HASH:=4280c24049fb25743a8876b7437e4954fbeb00e58ce940c509cdcc70b7cea576 else ifeq ($(ARM_CPU_TYPE),armv5te-armel) - PKG_HASH:=390840c939cec718570cd8cbf50ed130fd02d7f75b80d74af251bd1ecd0f43a8 + PKG_HASH:=7ba5247c674ace48c88e9ea65c4cb6ab754a5e5dd1d4cd2911442018545c33a8 else ifeq ($(ARM_CPU_TYPE),armv7-armel) - PKG_HASH:=965f9e88398f42cf5535612b58629b89c058db46afaf3c814929a7cfc9fa4e3b + PKG_HASH:=6c7f4454b3100fc448149caf8cb97dbcb75b284bab93b53dc885a370c496cc29 else ifeq ($(ARM_CPU_TYPE),armv7-armhf) - PKG_HASH:=f47df37959f73b2188f89faf8c9a3217966ca4c1389f695886f263d28911e8fb + PKG_HASH:=41377aa71c2a2da4481112c45b0129f167b96bc4b0c55d3f77309b4b82001891 else PKG_HASH:=dummy endif else ifeq ($(ARCH),mips) - PKG_HASH:=318587b09b37d1ab8af96466e5191ce32c4dbc92294f5c55826ebe1a88c7bcab + PKG_HASH:=a08fae7eae3c852a8b6c310276c881f135eabf86c18f689edd134800dc270e71 else ifeq ($(ARCH),mipsel) - PKG_HASH:=fd1b12b7cd2034aa8afa89901d0d38784528f10921b6c0f806dac9dcbf523085 + PKG_HASH:=499134680d7667fd256425d0f86b846dc4d00023b2315c48ed5337a7dae65926 else ifeq ($(ARCH),powerpc64) - PKG_HASH:=24dab3f89f8930cb925ec1a20d73111e09bae7e6a87954b56a681395660f6ed4 + PKG_HASH:=f40f51453c274774bdf64a170fe3ea1d87cf3761ba4a679ca8f08bdd2819f2e4 else ifeq ($(ARCH),riscv64) - PKG_HASH:=916bf42c60c49df5c3ee6f4a60ce542d2b080788445b9bf47f3322eb9a5b66cf + PKG_HASH:=4b18915eab8e3435fb6cc1d50298ad84708b14921fb1b60713f37864140aa1ab else ifeq ($(ARCH),x86_64) - PKG_HASH:=4909cf2e80861d082c4d4870950259d8c6f14e606f9dfa16c92514f178f3ae40 + PKG_HASH:=aeab01d678f92f176ba41d08363765ac53719bdaaff26bbd0e93c4fdc12bea20 else PKG_HASH:=dummy endif diff --git a/v2ray-geodata/Makefile b/v2ray-geodata/Makefile index 98e8b671..d03b562d 100644 --- a/v2ray-geodata/Makefile +++ b/v2ray-geodata/Makefile @@ -21,13 +21,13 @@ define Download/geoip HASH:=1cba1f0982cf62502fa079c66047c3d0c608196da5b3305671e68f60e917a482 endef -GEOSITE_VER:=20260904020013 +GEOSITE_VER:=20260906035830 GEOSITE_FILE:=dlc.dat.$(GEOSITE_VER) define Download/geosite URL:=https://github.com/v2fly/domain-list-community/releases/download/$(GEOSITE_VER)/ URL_FILE:=dlc.dat FILE:=$(GEOSITE_FILE) - HASH:=f82f26c015f9726c763d96a5f658e5b31b285dc094a985e718051e421f350ed6 + HASH:=8d401b06cde720cdfa9eb0e03751cc73ded08c6d53fa80f92d3f24798382eb92 endef GEOSITE_IRAN_VER:=202608310142