diff --git a/autoshare-ksmbd/Makefile b/autoshare-ksmbd/Makefile index 2a087457..face98fc 100644 --- a/autoshare-ksmbd/Makefile +++ b/autoshare-ksmbd/Makefile @@ -9,7 +9,7 @@ include $(TOPDIR)/rules.mk PKG_NAME:=autoshare-ksmbd PKG_VERSION:=1 -PKG_RELEASE:=1 +PKG_RELEASE:=2 PKG_ARCH:=all include $(INCLUDE_DIR)/package.mk diff --git a/autoshare-ksmbd/files/20-smb-ksmbd b/autoshare-ksmbd/files/20-smb-ksmbd index 06de8508..790df8d9 100644 --- a/autoshare-ksmbd/files/20-smb-ksmbd +++ b/autoshare-ksmbd/files/20-smb-ksmbd @@ -1,12 +1,5 @@ #!/bin/sh -# -# D-Team Technology Co.,Ltd. ShenZhen -# 作者:Vic -# -# 警告:对着屏幕的哥们,我们允许你使用此脚本,但不允许你抹去作者的信息,请保留这段话。 -# - . /lib/functions.sh . /lib/functions/service.sh @@ -16,8 +9,9 @@ config_file="/etc/config/ksmbd" [ ! -f /etc/config/ksmbd ] && { echo " config globals - option 'workgroup' 'WORKGROUP' - option 'description' 'Ksmbd on OpenWrt' + option workgroup 'WORKGROUP' + option charset 'UTF-8' + option description 'Ksmbd on OpenWRT' " > $config_file } @@ -32,21 +26,27 @@ wait_for_init() { } smb_handle() { - config_get path $1 path - if [ "$path" = "$2" ] ;then - global=1 + local path + config_get path "$1" path + if [ -n "$path" ]; then + case "$path" in + "$2" | "$2"/*) global=1 ;; + esac + case "$2" in + "$path" | "$path"/*) global=1 ;; + esac fi } chk_en() { - config_get_bool autoshare $1 autoshare 1 - [ $autoshare -eq 0 ] && exit + config_get_bool autoshare "$1" autoshare 1 + [ "$autoshare" -eq 0 ] && exit } config_load ksmbd config_foreach chk_en ksmbd -device=`basename $DEVPATH` +device=`basename "$DEVPATH"` case "$ACTION" in add) @@ -55,6 +55,8 @@ case "$ACTION" in sd*);; md*);; hd*);; + mmcblk*);; + nvme*);; *) return;; esac @@ -62,22 +64,21 @@ case "$ACTION" in wait_for_init - [ "$(uci -q get ksmbd.@globals[0].autoshare)" == "1" ] && { - /etc/init.d/ksmbd restart - exit 0; - } - - cat /proc/mounts | grep -v '/boot\|/opt' | while read j + grep -v '/boot\|/opt' /proc/mounts | while read -r j do str=${j%% *} - if [ "$str" == $path ];then + if [ "$str" = "$path" ];then strr=${j#* } target=${strr%% *} global=0 - config_foreach smb_handle share $target + + config_load ksmbd + config_foreach smb_handle share "$target" + name=${target#*/mnt/} - name=$(echo $name | sed -e "s/^\///") - if [ $global -eq 0 ] ;then + name=$(echo "$name" | sed -e "s/^\///") + + if [ "$global" -eq 0 ] ;then echo -e "\n\nconfig share" >> $config_file echo -e "\toption auto '1'" >> $config_file echo -e "\toption name '$name'" >> $config_file @@ -93,27 +94,26 @@ case "$ACTION" in fi fi done - uci -q set ksmbd.@globals[0].autoshare='1' - uci commit ksmbd ;; remove) i=0 while true do - dev=`uci get ksmbd.@share[$i].device` - [ $? -ne 0 ] && break + if ! uci -q get ksmbd.@share[$i] >/dev/null; then + break + fi - [ "$dev" = "$device" ] && { - auto=`uci get ksmbd.@share[$i].auto` - [ $auto = "1" ] && { - mount_dir=`uci get ksmbd.@share[$i].name` + dev=$(uci -q get ksmbd.@share[$i].device) + if [ "$dev" = "$device" ]; then + auto=$(uci -q get ksmbd.@share[$i].auto) + if [ "$auto" = "1" ]; then uci delete ksmbd.@share[$i] - uci commit + uci commit ksmbd /etc/init.d/ksmbd reload return - } - } + fi + fi let i+=1 done ;; diff --git a/autoshare-samba/Makefile b/autoshare-samba/Makefile index 4fd2ad15..09198a70 100644 --- a/autoshare-samba/Makefile +++ b/autoshare-samba/Makefile @@ -9,7 +9,7 @@ include $(TOPDIR)/rules.mk PKG_NAME:=autoshare-samba PKG_VERSION:=1 -PKG_RELEASE:=1 +PKG_RELEASE:=2 PKG_ARCH:=all include $(INCLUDE_DIR)/package.mk diff --git a/autoshare-samba/files/20-smb b/autoshare-samba/files/20-smb index 1e2d3bec..236d07c3 100644 --- a/autoshare-samba/files/20-smb +++ b/autoshare-samba/files/20-smb @@ -1,12 +1,5 @@ #!/bin/sh -# -# D-Team Technology Co.,Ltd. ShenZhen -# 作者:Vic -# -# 警告:对着屏幕的哥们,我们允许你使用此脚本,但不允许你抹去作者的信息,请保留这段话。 -# - . /lib/functions.sh . /lib/functions/service.sh @@ -33,21 +26,27 @@ wait_for_init() { } smb_handle() { - config_get path $1 path - if [ "$path" = "$2" ] ;then - global=1 + local path + config_get path "$1" path + if [ -n "$path" ]; then + case "$path" in + "$2" | "$2"/*) global=1 ;; + esac + case "$2" in + "$path" | "$path"/*) global=1 ;; + esac fi } chk_en() { - config_get_bool autoshare $1 autoshare 1 - [ $autoshare -eq 0 ] && exit + config_get_bool autoshare "$1" autoshare 1 + [ "$autoshare" -eq 0 ] && exit } config_load samba4 config_foreach chk_en samba4 -device=`basename $DEVPATH` +device=`basename "$DEVPATH"` case "$ACTION" in add) @@ -56,6 +55,8 @@ case "$ACTION" in sd*);; md*);; hd*);; + mmcblk*);; + nvme*);; *) return;; esac @@ -63,22 +64,21 @@ case "$ACTION" in wait_for_init - [ "$(uci -q get samba4.@samba[0].autoshare)" == "1" ] && { - /etc/init.d/samba4 restart - exit 0; - } - - cat /proc/mounts | grep -v '/boot\|/opt' | while read j + grep -v '/boot\|/opt' /proc/mounts | while read -r j do str=${j%% *} - if [ "$str" == $path ];then + if [ "$str" = "$path" ];then strr=${j#* } target=${strr%% *} global=0 - config_foreach smb_handle sambashare $target + + config_load samba4 + config_foreach smb_handle sambashare "$target" + name=${target#*/mnt/} - name=$(echo $name | sed -e "s/^\///") - if [ $global -eq 0 ] ;then + name=$(echo "$name" | sed -e "s/^\///") + + if [ "$global" -eq 0 ] ;then echo -e "\n\nconfig sambashare" >> $config_file echo -e "\toption auto '1'" >> $config_file echo -e "\toption name '$name'" >> $config_file @@ -94,27 +94,26 @@ case "$ACTION" in fi fi done - uci -q set samba4.@samba[0].autoshare='1' - uci commit samba4 ;; remove) i=0 while true do - dev=`uci get samba4.@sambashare[$i].device` - [ $? -ne 0 ] && break + if ! uci -q get samba4.@sambashare[$i] >/dev/null; then + break + fi - [ "$dev" = "$device" ] && { - auto=`uci get samba4.@sambashare[$i].auto` - [ $auto = "1" ] && { - mount_dir=`uci get samba4.@sambashare[$i].name` + dev=$(uci -q get samba4.@sambashare[$i].device) + if [ "$dev" = "$device" ]; then + auto=$(uci -q get samba4.@sambashare[$i].auto) + if [ "$auto" = "1" ]; then uci delete samba4.@sambashare[$i] - uci commit + uci commit samba4 /etc/init.d/samba4 reload return - } - } + fi + fi let i+=1 done ;; diff --git a/luci-app-amlogic/Makefile b/luci-app-amlogic/Makefile index 3fa63848..eb70a385 100644 --- a/luci-app-amlogic/Makefile +++ b/luci-app-amlogic/Makefile @@ -16,8 +16,8 @@ include $(TOPDIR)/rules.mk PKG_NAME:=luci-app-amlogic -PKG_VERSION:=3.1.286 -PKG_RELEASE:=1 +PKG_VERSION:=3.1.287 +PKG_RELEASE:=2 PKG_LICENSE:=GPL-2.0 License PKG_MAINTAINER:=ophub diff --git a/luci-app-amlogic/root/usr/share/amlogic/amlogic_check_plugin.sh b/luci-app-amlogic/root/usr/share/amlogic/amlogic_check_plugin.sh index 5efc3822..0f18f14c 100755 --- a/luci-app-amlogic/root/usr/share/amlogic/amlogic_check_plugin.sh +++ b/luci-app-amlogic/root/usr/share/amlogic/amlogic_check_plugin.sh @@ -159,6 +159,16 @@ else [[ "${?}" -ne "0" ]] && tolog "02.05 Language pack [ ${langfile} ] download failed." "1" done + # The .apk filename is preceded by a tilde (~) instead of a dot (.). + for file in ${TMP_CHECK_DIR}/*.apk; do + [[ -f "${file}" ]] || continue + base_name="$(basename "${file}")" + new_name="$(echo "${base_name}" | sed -E 's/\.([a-f0-9]{7}\.apk)/~\1/')" + if [[ "${base_name}" != "${new_name}" ]]; then + mv -f "${file}" "${TMP_CHECK_DIR}/${new_name}" || true + fi + done + sync && sleep 2 fi diff --git a/luci-app-openclawmgr/Makefile b/luci-app-openclawmgr/Makefile index 3a62fc5f..b51829eb 100644 --- a/luci-app-openclawmgr/Makefile +++ b/luci-app-openclawmgr/Makefile @@ -1,8 +1,8 @@ include $(TOPDIR)/rules.mk -PKG_VERSION:=0.1.5 -PKG_RELEASE:=6 +PKG_VERSION:=0.1.6 +PKG_RELEASE:=7 LUCI_TITLE:=LuCI support for OpenClaw Launcher LUCI_PKGARCH:=all diff --git a/luci-app-openclawmgr/htdocs/luci-static/resources/openclawmgr/app.js b/luci-app-openclawmgr/htdocs/luci-static/resources/openclawmgr/app.js index 9c88740f..60b25631 100644 --- a/luci-app-openclawmgr/htdocs/luci-static/resources/openclawmgr/app.js +++ b/luci-app-openclawmgr/htdocs/luci-static/resources/openclawmgr/app.js @@ -130,12 +130,14 @@ if (status.installing) return "安装中"; if (!status || !status.installed) return "未安装"; if (status.running) return "运行中"; + if (status.reachable) return "运行中(未托管)"; return "已停止"; } function statusDotClass(status) { if (status && status.installing) return ""; if (status && status.running) return "is-success"; + if (status && status.reachable) return ""; return "is-danger"; } @@ -382,11 +384,8 @@ var savingAi = state.savingSection === "ai"; var savingAccess = state.savingSection === "access"; - var serviceButtons = showServiceActions ? ( - (!status.running ? '' : '') + - (status.running ? '' : '') + - '' - ) : ''; + var canStartService = !status.running && !status.reachable; + var canStopService = !!status.running; root.innerHTML = '' + @@ -415,7 +414,7 @@ '' + '' + '' + - '' + + '' + '' + '' + '
安装任务正在后台运行,点击“安装中”可继续查看日志。
' + @@ -438,7 +437,15 @@ ["auto", "自动"] ])) + fieldInput("数据目录", '' + baseDirOptions + '') + - '
' + serviceButtons + (state.lastAppliedAt ? '已于 ' + escapeHtml(state.lastAppliedAt) + ' 更新配置' : '') + '
' + + '
' + + '' + + '' + + '' + + '' + + '' + + '' + + (state.lastAppliedAt ? '已于 ' + escapeHtml(state.lastAppliedAt) + ' 更新配置' : '') + + '
' + '' + '
' + @@ -485,6 +492,7 @@ function updateStatusDom(status) { status = status || {}; + var gatewayActive = !!(status.running || status.reachable); function byId(id) { if (root.getElementById) return root.getElementById(id); @@ -565,15 +573,40 @@ else cancelInstallBtn.classList.add("oclm-hidden"); } if (openConsoleBtn) { - if (status.running) openConsoleBtn.classList.remove("oclm-hidden"); + if (gatewayActive) openConsoleBtn.classList.remove("oclm-hidden"); else openConsoleBtn.classList.add("oclm-hidden"); - openConsoleBtn.disabled = !(status.running && state.consoleReady); + openConsoleBtn.disabled = !state.consoleReady; openConsoleBtn.innerHTML = openclawIcon("oclm-button-icon") + (state.consoleReady ? "打开控制台" : "控制台准备中…"); } if (noteEl) { if (status.installing) noteEl.classList.remove("oclm-hidden"); else noteEl.classList.add("oclm-hidden"); } + + var svcWrap = byId("oclm-service-actions"); + var btnStart = byId("oclm-btn-start"); + var btnStop = byId("oclm-btn-stop"); + var btnRestart = byId("oclm-btn-restart"); + var showServiceActions = !!(status.installed && !status.installing); + var canStartService = !!(!status.running && !status.reachable); + var canStopService = !!status.running; + + if (svcWrap) { + if (showServiceActions) svcWrap.classList.remove("oclm-hidden"); + else svcWrap.classList.add("oclm-hidden"); + } + if (btnStart) { + if (showServiceActions && canStartService) btnStart.classList.remove("oclm-hidden"); + else btnStart.classList.add("oclm-hidden"); + } + if (btnStop) { + if (showServiceActions && canStopService) btnStop.classList.remove("oclm-hidden"); + else btnStop.classList.add("oclm-hidden"); + } + if (btnRestart) { + if (showServiceActions) btnRestart.classList.remove("oclm-hidden"); + else btnRestart.classList.add("oclm-hidden"); + } } function fieldInput(label, control) { @@ -954,7 +987,7 @@ function refreshStatus(done) { request(config.statusUrl).then(function(data) { state.status = data || {}; - if (state.status && state.status.running) { + if (state.status && (state.status.running || state.status.reachable)) { if (!state.consoleReady) { pollConsoleReady(20); } diff --git a/luci-app-openclawmgr/luasrc/controller/openclawmgr.lua b/luci-app-openclawmgr/luasrc/controller/openclawmgr.lua index f6598e6d..64def85c 100644 --- a/luci-app-openclawmgr/luasrc/controller/openclawmgr.lua +++ b/luci-app-openclawmgr/luasrc/controller/openclawmgr.lua @@ -353,6 +353,46 @@ local function get_pid_uptime_human(pid) return fmt_elapsed(elapsed) end +local function probe_gateway_ready(port, base_dir, bind) + local sys = require "luci.sys" + local util = require "luci.util" + + if not port or not tostring(port):match("^%d+$") then + return false, "", "" + end + if not base_dir or base_dir == "" then + return false, "", "" + end + + local base_path = configured_base_path(base_dir) + local candidates = {} + + if bind == "lan" or bind == "auto" then + local ip = lan_ipv4() + if ip ~= "" then + candidates[#candidates + 1] = ip + end + end + candidates[#candidates + 1] = "127.0.0.1" + + local last_code, last_url = "", "" + for _, host in ipairs(candidates) do + local url = "http://" .. host .. ":" .. port .. base_path + last_url = url + local cmd = string.format( + "curl -fsS -o /dev/null --connect-timeout 1 --max-time 2 -w '%%{http_code}' %s 2>/dev/null", + util.shellquote(url) + ) + local code = sys.exec(cmd):gsub("%s+$", "") + last_code = code + local n = tonumber(code) or 0 + if n >= 200 and n < 400 then + return true, code, url + end + end + return false, last_code, last_url +end + function action_status() local sys = require "luci.sys" local uci = require "luci.model.uci".cursor() @@ -373,6 +413,11 @@ function action_status() running = (st == "running") installed = (st == "running" or st == "stopped") end + + local reachable, reachable_code, reachable_url = false, "", "" + if installed and not running then + reachable, reachable_code, reachable_url = probe_gateway_ready(port, base_dir, bind) + end local lock_running, lock_pid = installer_lock_running() local installing = (task.running and (task.op == "install" or task.op == "upgrade")) or lock_running if not installing and task.running and not installed and not running then @@ -405,6 +450,9 @@ function action_status() enabled = enabled, installed = installed, running = running, + reachable = reachable, + reachable_http_code = reachable_code, + reachable_url = reachable_url, task_running = task.running, task_op = task.op, installing = installing, @@ -423,11 +471,10 @@ function action_status() end function action_ready() - local sys = require "luci.sys" - local util = require "luci.util" local uci = require "luci.model.uci".cursor() local port = uci:get("openclawmgr", "main", "port") or "18789" + local bind = uci:get("openclawmgr", "main", "bind") or "lan" local base_dir = uci:get("openclawmgr", "main", "base_dir") or "" if not port:match("^%d+$") then port = "18789" end @@ -437,20 +484,13 @@ function action_ready() return end - local base_url = "http://127.0.0.1:" .. port .. configured_base_path(base_dir) - local cmd = string.format( - "curl -fsS -o /dev/null --connect-timeout 1 --max-time 2 -w '%%{http_code}' %s 2>/dev/null", - util.shellquote(base_url) - ) - local code = sys.exec(cmd):gsub("%s+$", "") - local n = tonumber(code) or 0 - local ready = (n >= 200 and n < 400) + local ready, code, url = probe_gateway_ready(port, base_dir, bind) write_json({ ok = true, ready = ready, http_code = code, - url = base_url, + url = url, }) end diff --git a/luci-app-openclawmgr/root/usr/libexec/istorec/openclawmgr.sh b/luci-app-openclawmgr/root/usr/libexec/istorec/openclawmgr.sh index d8cc1edb..399c462e 100755 --- a/luci-app-openclawmgr/root/usr/libexec/istorec/openclawmgr.sh +++ b/luci-app-openclawmgr/root/usr/libexec/istorec/openclawmgr.sh @@ -559,15 +559,14 @@ if selected_env_key and selected_api_key ~= "" then end cfg.env = next(env) and env or nil -local models = {} -models.mode = "merge" -local providers = {} -models.providers = providers -cfg.models = models + local models = ensure_table(cfg, "models") + if type(models.mode) ~= "string" or models.mode == "" then + models.mode = "merge" + end + local providers = ensure_table(models, "providers") -local current_provider_id = os.getenv("DEFAULT_AGENT_NAME") or "anthropic" -local current_provider = {} -providers[current_provider_id] = current_provider + local current_provider_id = os.getenv("DEFAULT_AGENT_NAME") or "anthropic" + local current_provider = ensure_table(providers, current_provider_id) if current_provider_id == "openai" then current_provider.api = "openai-completions" @@ -600,13 +599,30 @@ elseif current_provider_id == "moonshot" then } end -local override_base = os.getenv("DEFAULT_AGENT_OVERRIDE_BASE_URL") or "" -local base_url_mode = os.getenv("DEFAULT_AGENT_BASE_URL_MODE") or "default" -if base_url_mode == "override" and override_base ~= "" then - current_provider.baseUrl = override_base -elseif base_url_mode == "default" then - current_provider.baseUrl = os.getenv("DEFAULT_AGENT_BASE_URL") or current_provider.baseUrl -end + local override_base = os.getenv("DEFAULT_AGENT_OVERRIDE_BASE_URL") or "" + local base_url_mode = os.getenv("DEFAULT_AGENT_BASE_URL_MODE") or "default" + if base_url_mode == "override" and override_base ~= "" then + current_provider.baseUrl = override_base + elseif base_url_mode == "default" then + current_provider.baseUrl = os.getenv("DEFAULT_AGENT_BASE_URL") or current_provider.baseUrl + end + + -- luci.jsonc encodes empty Lua tables as JSON arrays ([]). To avoid producing + -- invalid provider entries like {"deepseek/deepseek-chat":[]}, drop provider + -- records that are empty or look like a provider/model string. + do + local to_del = {} + for k, v in pairs(providers) do + if type(k) == "string" and k:find("/", 1, true) then + table.insert(to_del, k) + elseif type(v) == "table" and next(v) == nil then + table.insert(to_del, k) + end + end + for _, k in ipairs(to_del) do + providers[k] = nil + end + end local agents = ensure_table(cfg, "agents") local defaults = ensure_table(agents, "defaults") diff --git a/luci-app-openclawmgr/root/usr/share/openclawmgr/oc-config.sh b/luci-app-openclawmgr/root/usr/share/openclawmgr/oc-config.sh new file mode 100755 index 00000000..cdd43576 --- /dev/null +++ b/luci-app-openclawmgr/root/usr/share/openclawmgr/oc-config.sh @@ -0,0 +1,6 @@ +#!/bin/sh +# Backward-compatible wrapper. New entry: openclawmgr-cli.sh + +set -eu + +exec /usr/share/openclawmgr/openclawmgr-cli.sh "$@" diff --git a/luci-app-passwall/luasrc/view/passwall/node_list/link_add_node.htm b/luci-app-passwall/luasrc/view/passwall/node_list/link_add_node.htm index d7ee0966..edbbec22 100644 --- a/luci-app-passwall/luasrc/view/passwall/node_list/link_add_node.htm +++ b/luci-app-passwall/luasrc/view/passwall/node_list/link_add_node.htm @@ -40,6 +40,22 @@ local api = require "luci.passwall.api" sendNextChunk(); } + function decodeIfBase64(str) { + try { + let s = str.replace(/-/g, '+').replace(/_/g, '/'); + while (s.length % 4) s += '='; + const decoded = decodeURIComponent( + atob(s).split('').map(c => + '%' + c.charCodeAt(0).toString(16).padStart(2, '0') + ).join('') + ); + if (btoa(unescape(encodeURIComponent(decoded))).replace(/=+$/, '') === s.replace(/=+$/, '')) { + return decoded; + } + } catch (e) {} + return str; + } + function getBg(el) { if (!el) return null; const style = getComputedStyle(el); @@ -74,15 +90,14 @@ local api = require "luci.passwall.api" const group = (document.querySelector('#addlink_group_custom input[type="hidden"]')?.value || "default"); nodes_link = nodes_link.replace(/\t/g, "").replace(/\r\n|\r/g, "\n").replace(/<[^>]*>/g, '').trim(); if (nodes_link != "") { + nodes_link = decodeIfBase64(nodes_link); let s = nodes_link.split('://'); if (s.length > 1) { ajax_add_node(nodes_link, group); - } - else { + } else { alert("<%:Please enter the correct link.%>"); } - } - else { + } else { document.getElementById("nodes_link").focus(); } } @@ -93,8 +108,7 @@ local api = require "luci.passwall.api" function(x, data) { if(x && x.status == 200) { window.location.href = '<%=api.url("node_list")%>'; - } - else { + } else { alert("<%:Error%>"); } }); @@ -154,8 +168,7 @@ local api = require "luci.passwall.api" function(x, data) { if (x && x.status == 200) { window.location.href = '<%=api.url("node_list")%>'; - } - else { + } else { alert("<%:Error%>"); } }); diff --git a/luci-app-pppoe-user/Makefile b/luci-app-pppoe-user/Makefile deleted file mode 100644 index 9b5eaf16..00000000 --- a/luci-app-pppoe-user/Makefile +++ /dev/null @@ -1,100 +0,0 @@ -include $(TOPDIR)/rules.mk - -LUCI_TITLE:=PPPoE Server User Management (Modern JS/Ucode) -LUCI_DEPENDS:=+luci-compat +kmod-nft-connlimit +ucode +rpcd +ppp +ppp-mod-pppoe -LUCI_PKGARCH:=all -PKG_VERSION:=2.0 -PKG_RELEASE:=2 - -# 定义配置文件,升级时保留 -define Package/luci-app-pppoe-user/conffiles -/etc/config/pppoe-user -endef - -# 预安装脚本:清理旧版本残留 -define Package/luci-app-pppoe-user/preinst -#!/bin/sh -if [ -z "$${IPKG_INSTROOT}" ]; then - # 仅在运行时清理,安装到新根文件系统时不清理 - if [ -f /etc/init.d/pppoe-user ]; then - /etc/init.d/pppoe-user stop 2>/dev/null || true - fi - # 清理旧的 Lua 视图缓存 - rm -rf /tmp/luci-modulecache/* - rm -rf /tmp/luci-indexcache/* -fi -exit 0 -endef - -define Package/luci-app-pppoe-user/install - # --- 1. I18N (让 luci.mk 自动处理,或手动编译) --- - # 注意:现代 luci.mk 会自动扫描 po 目录并编译。 - # 如果必须手动指定,确保编译为 .lua 或 .json (视版本而定) - # 这里我们依赖 luci.mk 的默认行为,只需确保 po 目录存在即可。 - # 如果 luci.mk 没有自动安装,取消下面注释: - # $(INSTALL_DIR) $(1)/usr/lib/lua/luci/i18n - # $(foreach po,$(wildcard ./po/*/pppoe-user.po), \ - # $(call Build/install-po,$(po),$(1)/usr/lib/lua/luci/i18n) \ - # ) - - # --- 2. LuCI JS Views (Modern Path) --- - $(INSTALL_DIR) $(1)/usr/share/luci/static/resources/view/pppoe-user - $(INSTALL_DATA) ./htdocs/luci-static/resources/view/pppoe-user/*.js \ - $(1)/usr/share/luci/static/resources/view/pppoe-user/ - - # --- 3. Ucode Scripts --- - $(INSTALL_DIR) $(1)/usr/share/ucode - $(INSTALL_DATA) ./root/usr/share/ucode/*.uc \ - $(1)/usr/share/ucode/ - # 确保 ucode 脚本可执行 (虽然不是必须,因为通过解释器调用,但习惯上加上) - chmod 644 $(1)/usr/share/ucode/*.uc - - # --- 4. RPCD ACL --- - $(INSTALL_DIR) $(1)/usr/share/rpcd/acl.d - $(INSTALL_DATA) ./root/usr/share/rpcd/acl.d/*.json \ - $(1)/usr/share/rpcd/acl.d/ - - # --- 5. System Configuration --- - $(INSTALL_DIR) $(1)/etc/config - $(INSTALL_CONF) ./root/etc/config/pppoe-user \ - $(1)/etc/config/ - - # --- 6. Init Scripts --- - $(INSTALL_DIR) $(1)/etc/init.d - $(INSTALL_BIN) ./root/etc/init.d/pppoe-user \ - $(1)/etc/init.d/ - - # --- 7. PPP Scripts --- - $(INSTALL_DIR) $(1)/etc/ppp - $(INSTALL_BIN) ./root/etc/ppp/ip-up \ - $(1)/etc/ppp/ - $(INSTALL_BIN) ./root/etc/ppp/ip-down \ - $(1)/etc/ppp/ - - $(INSTALL_DIR) $(1)/etc/ppp/ip-up.d - $(INSTALL_BIN) ./root/etc/ppp/ip-up.d/* \ - $(1)/etc/ppp/ip-up.d/ - - $(INSTALL_DIR) $(1)/etc/ppp/ip-down.d - $(INSTALL_BIN) ./root/etc/ppp/ip-down.d/* \ - $(1)/etc/ppp/ip-down.d/ - - # --- 8. Hotplug Scripts --- - $(INSTALL_DIR) $(1)/etc/hotplug.d/iface - $(INSTALL_BIN) ./root/etc/hotplug.d/iface/* \ - $(1)/etc/hotplug.d/iface/ - - # --- 9. UCI Defaults --- - $(INSTALL_DIR) $(1)/etc/uci-defaults - $(INSTALL_BIN) ./root/etc/uci-defaults/pppoe-user \ - $(1)/etc/uci-defaults/ - - # --- 10. LuCI Menu --- - $(INSTALL_DIR) $(1)/usr/share/luci/menu.d - $(INSTALL_DATA) ./root/usr/share/luci/menu.d/*.json \ - $(1)/usr/share/luci/menu.d/ -endef - -# 调用标准的 luci.mk 构建规则 -# 它会自动处理 i18n 编译和元数据生成 -include $(TOPDIR)/feeds/luci/luci.mk diff --git a/luci-app-pppoe-user/ReadMe.md b/luci-app-pppoe-user/ReadMe.md deleted file mode 100644 index 0444ed4a..00000000 --- a/luci-app-pppoe-user/ReadMe.md +++ /dev/null @@ -1,4 +0,0 @@ -# luci-app-pppoe-user - -OpenWrt Luci PPPoE dial-up user management application -Need to be used with the installation of rp-pppoe software package diff --git a/luci-app-pppoe-user/htdocs/luci-static/resources/view/pppoe-user/downtimeuser.js b/luci-app-pppoe-user/htdocs/luci-static/resources/view/pppoe-user/downtimeuser.js deleted file mode 100644 index 5d7a0452..00000000 --- a/luci-app-pppoe-user/htdocs/luci-static/resources/view/pppoe-user/downtimeuser.js +++ /dev/null @@ -1,47 +0,0 @@ -'use strict'; -'require view'; -'require form'; -'require uci'; - -return view.extend({ - load: function() { - return uci.load('pppoe-user'); - }, - render: function() { - var users = uci.sections('pppoe-user', 'user'); - var downUsers = users.filter(u => u.enabled == '0'); - - var m = new form.Map('pppoe-user'); - var s = m.section(form.TableSection, 'downtime', _('Downtime User [%d]').format(downUsers.length)); - s.anonymous = true; - s.addremove = false; - - // 填充数据 - s.section = function(section_id) { - // 手动注入数据到 section - return this.super('section', section_id); - }; - - // 由于 TableSection 通常绑定 UCI,这里我们用 filter 后的数据比较麻烦 - // 简单做法:遍历 downUsers 手动构建表格,或者使用 GridSection 并过滤 render - // 这里采用直接构建表格的方式以保持清晰 - - return E('div', { class: 'cbi-map' }, [ - E('h2', _('Downtime User [%d]').format(downUsers.length)), - E('table', { class: 'table' }, [ - E('tr', { class: 'tr table-titles' }, [ - E('th', { class: 'th' }, _('Username')), - E('th', { class: 'th' }, _('MAC')), - E('th', { class: 'th' }, _('Package')), - E('th', { class: 'th' }, _('Expiration')) - ]), - ...downUsers.map(u => E('tr', { class: 'tr cbi-rowstyle-1' }, [ - E('td', { class: 'td' }, u.username), - E('td', { class: 'td' }, u.macaddr || '-'), - E('td', { class: 'td' }, u.package), - E('td', { class: 'td' }, u.expires) - ])) - ]) - ]); - } -}); diff --git a/luci-app-pppoe-user/htdocs/luci-static/resources/view/pppoe-user/interface.js b/luci-app-pppoe-user/htdocs/luci-static/resources/view/pppoe-user/interface.js deleted file mode 100644 index 3137cdbe..00000000 --- a/luci-app-pppoe-user/htdocs/luci-static/resources/view/pppoe-user/interface.js +++ /dev/null @@ -1,22 +0,0 @@ -'use strict'; -'require view'; -'require fs'; - -return view.extend({ - load: function() { - return fs.read('/var/pppoe-user/log/interface.log'); - }, - render: function(logs) { - var m = new form.Map('pppoe-user'); - var s = m.section(form.NamedSection, 'logview', 'dummy'); - - var o = s.option(form.TextValue, '_log'); - o.rows = 30; - o.readonly = true; - o.cfgvalue = function() { - return logs || _('No log available.'); - }; - - return m.render(); - } -}); diff --git a/luci-app-pppoe-user/htdocs/luci-static/resources/view/pppoe-user/onlineuser.js b/luci-app-pppoe-user/htdocs/luci-static/resources/view/pppoe-user/onlineuser.js deleted file mode 100644 index 29815cbd..00000000 --- a/luci-app-pppoe-user/htdocs/luci-static/resources/view/pppoe-user/onlineuser.js +++ /dev/null @@ -1,169 +0,0 @@ -'use strict'; -'require view'; -'require form'; -'require fs'; -'require ubus'; -'require ui'; - -return view.extend({ - load: function() { - return Promise.all([ - fs.read('/proc/net/dev'), - fs.list('/var/etc/pppoe-user/session') - ]).then(function(data) { - let devContent = data[0] || ''; - let files = data[1] || []; - - let pppCount = 0; - devContent.split(/\n/).forEach(function(line) { - if (/^\s*ppp/.test(line)) pppCount++; - }); - - let sessions = []; - let promises = files.map(function(f) { - if (f.name.endsWith('.json')) { - return fs.read('/var/etc/pppoe-user/session/' + f.name).then(function(content) { - try { - let obj = JSON.parse(content); - obj.session_file = '/var/etc/pppoe-user/session/' + f.name; - sessions.push(obj); - } catch(e) {} - }); - } - }); - - return Promise.all(promises).then(function() { - return { count: pppCount, sessions: sessions }; - }); - }); - }, - - render: function(data) { - var m, s, o; - var tableData = data.sessions || []; - - m = new form.Map('pppoe-user'); - s = m.section(form.TableSection, 'sessions', _('Online Users [%d]').format(data.count)); - s.anonymous = true; - s.addremove = false; - s.sortable = false; - s.nodescriptions = true; - - o = s.option(form.DummyValue, 'username', _('User Name')); - o = s.option(form.DummyValue, 'mac', _('MAC address')); - o = s.option(form.DummyValue, 'interface', _('Interface')); - o = s.option(form.DummyValue, 'ip', _('IP address')); - o = s.option(form.DummyValue, 'package', _('Package')); - o = s.option(form.DummyValue, 'updated', _('Renewal Date')); - o = s.option(form.DummyValue, 'uptime', _('Up Time')); - - var btn = s.option(form.Button, 'kill', _('Forced Offline')); - btn.inputstyle = 'reset'; - btn.onclick = function(ev, section_id) { - var row = tableData.find(function(r) { return r.username === section_id; }); - // 注意:TableSection 的 section_id 通常是配置名,这里我们是动态数据,需特殊处理 - // 更好的方式是将 username 作为 key 或使用 custom render - // 此处简化:假设我们能获取到 row 数据 - // 实际开发中,建议使用 form.GridSection 配合 load 数据映射,或者自定义 HTML - // 下面展示一种通过 data-attribute 传递参数的方法 - - // 由于 TableSection 处理动态非 UCI 数据较麻烦,这里采用更直接的 UI 构建方式 - // 或者在 render 中手动构建表格。 - // 为了保持代码简洁,这里演示核心逻辑: - - var targetRow = this.map.findElement('id', 'cbi.pppoe-user.sessions.' + section_id); - // 获取该行对应的真实数据 (需要在 render 时将数据绑定到 DOM 或全局变量) - // 鉴于复杂度,推荐直接使用 L.ui.showModal 或 自定义 renderContent - }; - - // 【更优解】:对于非 UCI 的动态列表,直接返回 HTML 往往比 form.TableSection 更灵活 - // 但为了保持风格统一,我们假设用户能理解需要自定义 onclick 来获取数据 - - // 重新实现 onclick 以获取正确数据 - btn.onclick = L.bind(function(ev, section_id) { - // 在 load 阶段我们将数据存到了 this.sessionsData - var session = this.sessionsData.find(s => s.username === section_id); - if (!session) return; - - return ui.showModal(_('Confirm'), [ - E('p', _('Are you sure you want to force offline user "%s"?').format(session.username)), - E('div', { class: 'right' }, [ - E('button', { - class: 'btn', - click: ui.hideModal - }, [ _('Cancel') ]), - ' ', - E('button', { - class: 'btn cbi-button-action', - click: ui.handler(L.bind(function() { - return ubus.call('pppoe.user', 'kill', { - session_file: session.session_file, - pid: session.pid - }).then(() => { - ui.addNotification(null, E('p', _('User killed successfully'))); - this.load().then(d => this.render(d)); // 刷新 - }).catch(e => { - ui.addNotification(null, E('p', _('Error: ') + e.message)); - }); - }, this)) - }, [ _('OK') ]) - ]) - ]); - }, this); - - // 需要将数据挂载到 this 以便 onclick 访问 - this.sessionsData = tableData; - - // 覆盖 renderSection 以正确映射 section_id 到 username - s.renderSection = function(section_id) { - // 这里的 section_id 其实是 username,因为我们在 load 后需要手动映射 - // 这种动态数据最好用 form.GridSection 的 load/save 钩子,或者直接手写 HTML - // 简单起见,这里假设 section_id 就是 username (需在 section 定义时指定) - return this.super('renderSection', section_id); - }; - - // 修正:TableSection 默认基于 UCI config。对于纯动态数据, - // 建议重写 render 函数直接生成 HTML 表格,不使用 form.Map 的自动渲染 - // 下面提供一个直接渲染 HTML 的版本思路: - - return E('div', { class: 'cbi-map' }, [ - E('h2', _('Online Users [%d]').format(data.count)), - E('table', { class: 'table' }, [ - E('tr', { class: 'tr table-titles' }, [ - E('th', { class: 'th' }, _('User Name')), - E('th', { class: 'th' }, _('IP')), - E('th', { class: 'th' }, _('MAC')), - E('th', { class: 'th' }, _('Action')) - ]), - ...tableData.map(user => E('tr', { class: 'tr cbi-rowstyle-' + ((Math.random() > 0.5) ? '1' : '2') }, [ - E('td', { class: 'td' }, user.username), - E('td', { class: 'td' }, user.ip), - E('td', { class: 'td' }, user.mac), - E('td', { class: 'td' }, [ - E('button', { - class: 'btn cbi-button-action', - click: () => { - // 同上 kill 逻辑 - ui.showModal(_('Confirm'), [ - E('p', _('Kill %s?').format(user.username)), - E('div', { class: 'right' }, [ - E('button', { class: 'btn', click: ui.hideModal }, _('Cancel')), - ' ', - E('button', { - class: 'btn cbi-button-negative', - click: () => { - ubus.call('pppoe.user', 'kill', { session_file: user.session_file, pid: user.pid }) - .then(() => location.reload()) - .catch(e => alert(e)); - } - }, _('Kill')) - ]) - ]); - } - }, _('Forced Offline')) - ]) - ])) - ]) - ]); - } -}); diff --git a/luci-app-pppoe-user/htdocs/luci-static/resources/view/pppoe-user/onu.js b/luci-app-pppoe-user/htdocs/luci-static/resources/view/pppoe-user/onu.js deleted file mode 100644 index 105b5b80..00000000 --- a/luci-app-pppoe-user/htdocs/luci-static/resources/view/pppoe-user/onu.js +++ /dev/null @@ -1,109 +0,0 @@ -'use strict'; -'require view'; -'require form'; -'require ui'; - -return view.extend({ - render: function () { - var m, s, o; - - m = new form.Map('pppoe-user', _(''), _('')); - - // User section - s = m.section(form.GridSection, 'user', _('ONU List')); - s.anonymous = true; - s.nodescriptions = true - - // Username field - o = s.option(form.Value, 'username', _('Account')); - o.readonly = true; - - // Service Name field (dropdown) - o = s.option(form.Value, 'servicename', _('Service Name')); - o.modalonly = true; - o.readonly = true; - - // Password field - o = s.option(form.Value, 'password', _('Password')); - o.password = true; - o.modalonly = true; - o.readonly = true; - - // MAC Address field (dropdown) - o = s.option(form.Value, 'macaddr', _('MAC Address')); - o.password = true; - o.modalonly = true; - o.readonly = true; - - // Package field (dropdown) - o = s.option(form.Value, 'package', _('Package')); - o.readonly = true; - - // Upload Speed field (dropdown) - var o = s.option(form.Value, 'urate', _('Upload Speed')); - o.modalonly = true; - o.readonly = true; - - // Download Speed field (dropdown) - var o = s.option(form.Value, 'drate', _('Download Speed')); - o.modalonly = true; - o.readonly = true; - - // Concurrent Connection Number Segment (dropdown menu) - var o = s.option(form.Value, 'connect', _('Connection Number')); - o.modalonly = true; - o.readonly = true; - - // OLT field (dropdown) - var o = s.option(form.Value, 'olt', _('OLT')); - o.value('1', 'OLT 1'); - o.value('2', 'OLT 2'); - o.value('3', 'OLT 3'); - o.value('4', 'OLT 4'); - o.value('5', 'OLT 5'); - o.value('6', 'OLT 6'); - o.value('7', 'OLT 7'); - o.value('8', 'OLT 8'); - o.value('9', 'OLT 9'); - o.value('10', 'OLT 10'); - o.value('11', 'OLT 11'); - o.value('12', 'OLT 12'); - o.value('13', 'OLT 13'); - o.value('14', 'OLT 14'); - o.value('15', 'OLT 15'); - o.value('16', 'OLT 16'); - o.value('17', 'OLT 17'); - o.value('18', 'OLT 18'); - o.value('19', 'OLT 19'); - o.value('20', 'OLT 20'); - - // PON field (dropdown) - var o = s.option(form.Value, 'pon', _('PON')); - o.value('1', 'PON 1'); - o.value('2', 'PON 2'); - o.value('3', 'PON 3'); - o.value('4', 'PON 4'); - o.value('5', 'PON 5'); - o.value('6', 'PON 6'); - o.value('7', 'PON 7'); - o.value('8', 'PON 8'); - o.value('9', 'PON 9'); - o.value('10', 'PON 10'); - o.value('11', 'PON 11'); - o.value('12', 'PON 12'); - o.value('13', 'PON 13'); - o.value('14', 'PON 14'); - o.value('15', 'PON 15'); - o.value('16', 'PON 16'); - - // Serial Number field (dropdown) - o = s.option(form.Value, 'sn', _('S/N')); - o.placeholder = 'ONT Serial Number'; - - // Submit button - return m.render(); - }, - handleSave: null, - handleSaveApply: null, - handleReset: null -}); diff --git a/luci-app-pppoe-user/htdocs/luci-static/resources/view/pppoe-user/user.js b/luci-app-pppoe-user/htdocs/luci-static/resources/view/pppoe-user/user.js deleted file mode 100644 index e664906f..00000000 --- a/luci-app-pppoe-user/htdocs/luci-static/resources/view/pppoe-user/user.js +++ /dev/null @@ -1,466 +0,0 @@ -'use strict'; -'require view'; -'require form'; -'require ui'; -'require uci'; - -return view.extend({ - render: function () { - var m, s, o; - - m = new form.Map('pppoe-user', _(''), _('')); - - // User section - s = m.section(form.GridSection, 'user', _('User List')); - s.anonymous = true; - s.addremove = true; - s.editable = false; - s.nodescriptions = true; - s.sortable = false; - - // Enable User field - o = s.option(form.Flag, 'enabled', _('Enable')); - o.rmempty = false; // Make the field required - o.default = "1"; - - // Username field - o = s.option(form.Value, 'username', _('Account')); - o.rmempty = false; // Make the field required - o.placeholder = _('Required'); - o.validate = function (section_id, value) { - // Validate the input value to match the alphanumeric format - if (!/^[a-zA-Z0-9]+$/.test(value)) { - return _('Invalid username format. Only alphanumeric characters are allowed.'); - } - - // Check the length of the username - if (value.length < 4 || value.length > 16) { - return _('Username length must be between 4 and 16 characters.'); - } - - // Check for duplicate usernames - var otherSections = uci.sections('pppoe-user', 'user'); - for (var i = 0; i < otherSections.length; i++) { - if (section_id !== otherSections[i]['.name'] && otherSections[i]['username'] === value) { - return _('This username is already in use. Please choose a different one.'); - } - } - - return true; - }; - - // Service Name field (dropdown) - o = s.option(form.ListValue, 'servicename', _('Service Name')); - o.readonly = true; - o.modalonly = true; - o.value('*', _('Default')); - o.value('serviceA', 'Service A'); - o.value('serviceB', 'Service B'); - o.value('serviceC', 'Service C'); - - // Password field - o = s.option(form.Value, 'password', _('Password')); - o.placeholder = _('Required'); - o.password = true; - o.modalonly = true; - o.validate = function (section_id, value) { - // Validate the input value to ensure it's between 8 and 16 characters - if (value.length < 8 || value.length > 16) { - return _('Password must be between 8 and 16 characters.'); - } - return true; - }; - - var currentDate = new Date(); - var year = currentDate.getFullYear(); - var month = ('0' + (currentDate.getMonth() + 1)).slice(-2); - var day = ('0' + currentDate.getDate()).slice(-2); - var formattedDate = year + month + day; - - o.default = formattedDate.slice(0, 8); - o.rmempty = false; // Make the field required - - var currentDate = new Date(); - var year = currentDate.getFullYear(); - var month = ('0' + (currentDate.getMonth() + 1)).slice(-2); - var day = ('0' + currentDate.getDate()).slice(-2); - var formattedDate = year + month + day; - - o.default = formattedDate.slice(0, 8); - o.rmempty = false; // Make the field required - - // IP Address field (custom input with default value *) - o = s.option(form.Value, 'ipaddr', _('IP Address')); - o.placeholder = _('Required'); - o.default = '*'; - o.value('172.31.16.0/23', '172.31.16.0/23 Rules WAN1'); - o.value('172.31.26.0/23', '172.31.26.0/23 Rules WAN2'); - o.value('172.31.36.0/23', '172.31.36.0/23 Rules WAN3'); - o.value('172.31.46.0/23', '172.31.46.0/23 Rules WAN4'); - o.value('172.31.56.0/23', '172.31.56.0/23 Rules WAN5'); - o.value('172.31.66.0/23', '172.31.66.0/23 Rules WAN6'); - o.value('172.31.76.0/23', '172.31.76.0/23 Rules WAN7'); - o.value('172.31.86.0/23', '172.31.86.0/23 Rules WAN8'); - o.value('172.31.96.0/23', '172.31.96.0/23 Rules WAN9'); - o.value('*', _('Default')); - o.value('172.31.100.0/23', '172.31.100.0/23 Rules MAN0'); - o.value('172.31.110.0/23', '172.31.110.0/23 Rules MAN1'); - o.value('172.31.120.0/23', '172.31.120.0/23 Rules MAN2'); - o.value('172.31.130.0/23', '172.31.130.0/23 Rules MAN3'); - o.value('172.31.140.0/23', '172.31.140.0/23 Rules MAN4'); - o.value('172.31.150.0/23', '172.31.150.0/23 Rules MAN5'); - o.value('172.31.160.0/23', '172.31.160.0/23 Rules MAN6'); - o.value('172.31.170.0/23', '172.31.170.0/23 Rules MAN7'); - o.value('172.31.180.0/23', '172.31.180.0/23 Rules MAN8'); - o.value('172.31.190.0/23', '172.31.190.0/23 Rules MAN9'); - - // Enhanced validation for IP addresses and network segments - o.validate = function (section_id, value) { - // Regular expressions for valid IPv4, IPv6, and network segments - var ipv4Regex = /^(\d{1,3}\.){3}\d{1,3}$/; - var ipv6Regex = /^[\da-fA-F]{1,4}(:[\da-fA-F]{1,4}){0,7}$/; - var networkSegmentRegex = /^(\d{1,3}\.){3}\d{1,3}\/(\d{1,2})$/; - - if (value === '*') { - return true; // Asterisk is allowed - } - - if (ipv4Regex.test(value)) { - // Validate IPv4 address - var parts = value.split('.'); - for (var i = 0; i < 4; i++) { - var part = parseInt(parts[i]); - if (part < 0 || part > 255) { - return _('Invalid IPv4 address. Each segment must be between 0 and 255.'); - } - } - // Check for duplicate IP addresses - var otherSections = uci.sections('pppoe-user', 'user'); - for (var i = 0; i < otherSections.length; i++) { - if (section_id !== otherSections[i]['.name'] && otherSections[i]['ipaddr'] === value) { - return _('This IP address is already in use by another user. Please choose a different one.'); - } - } - return true; - } - - if (ipv6Regex.test(value)) { - // Validate IPv6 address - var otherSections = uci.sections('pppoe-user', 'user'); - for (var i = 0; i < otherSections.length; i++) { - if (section_id !== otherSections[i]['.name'] && otherSections[i]['ipaddr'] === value) { - return _('This IP address is already in use by another user. Please choose a different one.'); - } - } - return true; - } - - if (networkSegmentRegex.test(value)) { - // Validate IP network segment - var subnetParts = value.split('/'); - var subnetSize = parseInt(subnetParts[1]); - - if (subnetSize < 0 || subnetSize > 32) { - return _('Invalid subnet size. It must be between 0 and 32.'); - } - - return true; - } - - // Invalid input, return an error message - return _('Invalid input. Please use "*" or a valid IPv4, IPv6 address, or IP network segment.'); - }; - - // Automatically generate an available IP address when a network segment is selected - o.cfgvalue = function (section_id) { - var selectedSegment = uci.get('pppoe-user', section_id, 'ipaddr'); - - // Add your new predefined network segment here - if (selectedSegment === '172.31.16.0/23' || - selectedSegment === '172.31.26.0/23' || - selectedSegment === '172.31.36.0/23' || - selectedSegment === '172.31.46.0/23' || - selectedSegment === '172.31.56.0/23' || - selectedSegment === '172.31.66.0/23' || - selectedSegment === '172.31.76.0/23' || - selectedSegment === '172.31.86.0/23' || - selectedSegment === '172.31.96.0/23' || - selectedSegment === '172.31.100.0/23' || - selectedSegment === '172.31.110.0/23' || - selectedSegment === '172.31.120.0/23' || - selectedSegment === '172.31.130.0/23' || - selectedSegment === '172.31.140.0/23' || - selectedSegment === '172.31.150.0/23' || - selectedSegment === '172.31.160.0/23' || - selectedSegment === '172.31.170.0/23' || - selectedSegment === '172.31.180.0/23' || - selectedSegment === '172.31.190.0/23') { - // Generate and assign an available IP address based on the selected network segment - var generatedIP = getAvailableIPAddress(selectedSegment); - - // Check if the generated IP address already exists in other sections - var otherSections = uci.sections('pppoe-user', 'user'); - for (var i = 0; i < otherSections.length; i++) { - if (section_id !== otherSections[i]['.name'] && otherSections[i]['ipaddr'] === generatedIP) { - generatedIP = getAvailableIPAddress(selectedSegment); // Regenerate if it's a duplicate - } - } - - uci.set('pppoe-user', section_id, 'ipaddr', generatedIP); - uci.save(); - return generatedIP; - } - return selectedSegment; - }; - - // Define a function to generate an available IP address based on the selected network segment - function getAvailableIPAddress(networkSegment) { - // Basic logic to generate an available IP address within the network segment - var segments = networkSegment.split('/'); - var baseIP = segments[0]; - var subnetSize = parseInt(segments[1]); - - // Generate a random host number within the subnet range (exclude network and broadcast) - var hostNumber = Math.floor(Math.random() * (Math.pow(2, 32 - subnetSize) - 2)) + 1; - - // Convert the host number to an IP address - var generatedIP = baseIP.split('.').map(function (part, index) { - return parseInt(part) + (hostNumber >> (8 * (3 - index)) & 255); - }).join('.'); - - return generatedIP; - }; - - // MAC Address field (dropdown) - o = s.option(form.Value, 'macaddr', _('MAC Address')); - o.modalonly = true; - o.placeholder = 'MAC Address'; - o.password = true; - o.readonly = true; - - // Package field (dropdown) - o = s.option(form.ListValue, 'package', _('Package')); - o.default = 'home'; - o.value('home', _('Home')); - o.value('office', _('Office')); - o.value('free', _('Free')); - o.value('test', _('Test')); - - // Enable QoS field (checkbox) - o = s.option(form.Flag, 'qos', _('QoS')); - o.modalonly = true; - o.default = '0'; - o.readonly = false; - o.rmempty = false; - - // Upload Speed field (dropdown) - var o = s.option(form.Value, 'urate', _('Upload Speed')); - o.modalonly = true; - o.placeholder = "1 to 10000 Mbps" - o.datatype = "range(1,10000)" - o.default = '15'; - o.depends('qos', '1'); // Show only when QoS is enabled - - // Download Speed field (dropdown) - var o = s.option(form.Value, 'drate', _('Download Speed')); - o.modalonly = true; - o.placeholder = "1 to 10000 Mbps" - o.datatype = "range(1,10000)" - o.default = '30'; - o.depends('qos', '1'); // Show only when QoS is enabled - - // Concurrent Connection Number Segment (dropdown menu) - var o = s.option(form.ListValue, 'connect', _('Connection Number')); - o.modalonly = true; - o.datatype = "range(128,1048576)" - o.default = '2048'; - o.value('128'); - o.value('256'); - o.value('512'); - o.value('1024'); - o.value('2048'); - o.value('4096'); - o.value('8192'); - o.value('16384'); - o.value('32768'); - o.value('65536'); - o.value('131072'); - o.value('262144'); - o.value('524288'); - o.value('1048576'); - o.depends('qos', '1'); // Show only when QoS is enabled - - // Opening Date field (read-only) - o = s.option(form.Value, 'opening', _('Opening Date')); - o.readonly = true; - // Automatically populate with the current date in 'YYYY-MM-DD' format - var currentDate = new Date(); - var year = currentDate.getFullYear(); - var month = ('0' + (currentDate.getMonth() + 1)).slice(-2); - var day = ('0' + currentDate.getDate()).slice(-2); - var formattedDate = year + '-' + month + '-' + day; - o.default = formattedDate; - - // Expiration Date field - o = s.option(form.Value, 'expires', _('Expiration Date')); - o.datatype = 'string'; - o.placeholder = 'YYYY-MM-DD'; - o.validate = function (section_id, value) { - // Validate the input value to match the format YYYY-MM-DD - if (!/^\d{4}-\d{2}-\d{2}$/.test(value)) { - return _('Invalid date format. Please use YYYY-MM-DD.'); - } - - // Split the date string into year, month, and day components - var parts = value.split('-'); - var year = parseInt(parts[0], 10); - var month = parseInt(parts[1], 10); - var day = parseInt(parts[2], 10); - - // Validate the year, month, and day values - if (isNaN(year) || isNaN(month) || isNaN(day)) { - return _('Invalid date values.'); - } - - if (month < 1 || month > 12) { - return _('Invalid month. Please use a value between 01 and 12.'); - } - - var daysInMonth = new Date(year, month, 0).getDate(); - if (day < 1 || day > daysInMonth) { - return _('Invalid day. Please use a value between 01 and ') + daysInMonth + '.'; - } - - return true; - }; - var currentDate = new Date(); - - // Calculate expiration dates based on the current day + 1/3/6/12 months - var oneMonthLater = new Date(currentDate); - oneMonthLater.setMonth(currentDate.getMonth() + 1); - var threeMonthsLater = new Date(currentDate); - threeMonthsLater.setMonth(currentDate.getMonth() + 3); - var sixMonthsLater = new Date(currentDate); - sixMonthsLater.setMonth(currentDate.getMonth() + 6); - var twelveMonthsLater = new Date(currentDate); - twelveMonthsLater.setMonth(currentDate.getMonth() + 12); - - // Convert calculated dates to YYYY-MM-DD format - function formatDate(date) { - var year = date.getFullYear(); - var month = (date.getMonth() + 1).toString().padStart(2, '0'); - var day = date.getDate().toString().padStart(2, '0'); - return year + '-' + month + '-' + day; - } - // Set the default value to three months later - o.default = formatDate(threeMonthsLater); - o.value(formatDate(oneMonthLater), formatDate(oneMonthLater) + ' ' + _('One Month')); - o.value(formatDate(threeMonthsLater), formatDate(threeMonthsLater) + ' ' + _('Three Months')); - o.value(formatDate(sixMonthsLater), formatDate(sixMonthsLater) + ' ' + _('Six Months')); - o.value(formatDate(twelveMonthsLater), formatDate(twelveMonthsLater) + ' ' + _('Twelve Months')); - - // Enable Connect field (checkbox) - o = s.option(form.Flag, 'more', _('Contact')); - o.rmempty = false; // Make the field required - o.modalonly = true; - o.default = '0'; - - // Address field (dropdown) - o = s.option(form.Value, 'address', _('Address')); - o.modalonly = true; - o.placeholder = 'User Address'; - o.depends('more', '1'); - - // E-mail field (dropdown) - o = s.option(form.DynamicList, 'mail', _('E-mail')); - o.modalonly = true; - o.placeholder = 'User Email'; - o.depends('more', '1'); - - // Phone field (dropdown) - o = s.option(form.DynamicList, 'phone', _('Telephone')); - o.modalonly = true; - o.placeholder = 'User Phone'; - o.depends('more', '1'); - - // GPS Location field (dropdown) - o = s.option(form.Value, 'gps', _('GPS')); - o.placeholder = 'ONT GPS Location'; - o.modalonly = true; - o.depends('more', '1'); - - o = s.option(form.TextValue, 'notes', _('Notes'), _('')); - o.modalonly = true; - o.optional = true; - o.depends('more', '1'); - - // Agent field (dropdown) - o = s.option(form.Value, 'agent', _('Agent')); - o.modalonly = true; - o.readonly = true; - - // Enable ONT field (checkbox) - o = s.option(form.Flag, 'ont', _('ONT')); - o.rmempty = false; // Make the field required - o.modalonly = true; - o.default = '0'; - - // OLT field (dropdown) - var o = s.option(form.Value, 'olt', _('OLT')); - o.modalonly = true; - o.value('1', 'OLT 1'); - o.value('2', 'OLT 2'); - o.value('3', 'OLT 3'); - o.value('4', 'OLT 4'); - o.value('5', 'OLT 5'); - o.value('6', 'OLT 6'); - o.value('7', 'OLT 7'); - o.value('8', 'OLT 8'); - o.value('9', 'OLT 9'); - o.value('10', 'OLT 10'); - o.value('11', 'OLT 11'); - o.value('12', 'OLT 12'); - o.value('13', 'OLT 13'); - o.value('14', 'OLT 14'); - o.value('15', 'OLT 15'); - o.value('16', 'OLT 16'); - o.value('17', 'OLT 17'); - o.value('18', 'OLT 18'); - o.value('19', 'OLT 19'); - o.value('20', 'OLT 20'); - o.depends('ont', '1'); - - // PON field (dropdown) - var o = s.option(form.Value, 'pon', _('PON')); - o.modalonly = true; - o.value('1', 'PON 1'); - o.value('2', 'PON 2'); - o.value('3', 'PON 3'); - o.value('4', 'PON 4'); - o.value('5', 'PON 5'); - o.value('6', 'PON 6'); - o.value('7', 'PON 7'); - o.value('8', 'PON 8'); - o.value('9', 'PON 9'); - o.value('10', 'PON 10'); - o.value('11', 'PON 11'); - o.value('12', 'PON 12'); - o.value('13', 'PON 13'); - o.value('14', 'PON 14'); - o.value('15', 'PON 15'); - o.value('16', 'PON 16'); - o.depends('ont', '1'); - - // Serial Number field (dropdown) - o = s.option(form.Value, 'sn', _('S/N')); - o.modalonly = true; - o.placeholder = 'ONT Serial Number'; - o.depends('ont', '1'); - - // Submit button - return m.render(); - }, - handleSave: null, - handleSaveApply: null, - handleReset: null -}); diff --git a/luci-app-pppoe-user/po/templates/pppoe-user.pot b/luci-app-pppoe-user/po/templates/pppoe-user.pot deleted file mode 100644 index 44ca0c50..00000000 --- a/luci-app-pppoe-user/po/templates/pppoe-user.pot +++ /dev/null @@ -1,170 +0,0 @@ -msgid "" -msgstr "Content-Type: text/plain; charset=UTF-8" - -msgid "Management" -msgstr "" - -msgid "User Manager" -msgstr "" - -msgid "New Users" -msgstr "" - -msgid "User List" -msgstr "" - -msgid "ONU List" -msgstr "" - -msgid "User Number" -msgstr "" - -msgid "Enabled" -msgstr "" - -msgid "User Name" -msgstr "" - -msgid "Account" -msgstr "" - -msgid "This username is already in use. Please choose a different one." -msgstr "" - -msgid "Service Name" -msgstr "" - -msgid "Password" -msgstr "" - -msgid "Password must be between 8 and 16 characters." -msgstr "" - -msgid "IP Address" -msgstr "" - -msgid "Invalid IPv4 address. Each segment must be between 0 and 255." -msgstr "" - -msgid "This IP address is already in use by another user. Please choose a different one." -msgstr "" - -msgid "Invalid subnet size. It must be between 0 and 32." -msgstr "" - -msgid "Invalid input. Please use "*" or a valid IPv4, IPv6 address, or IP network segment." -msgstr "" - -msgid "Package" -msgstr "" - -msgid "Speed" -msgstr "" - -msgid "QoS" -msgstr "" - -msgid "Upload Speed" -msgstr "" - -msgid "Download Speed" -msgstr "" - -msgid "Speed Unit" -msgstr "" - -msgid "Connection Number" -msgstr "" - -msgid "Queuing Rules" -msgstr "" - -msgid "Queue Script" -msgstr "" - -msgid "Link Layer" -msgstr "" - -msgid "Overhead Bytes" -msgstr "" - -msgid "Debug Logging" -msgstr "" - -msgid "Opening Date" -msgstr "" - -msgid "Validity Period" -msgstr "" - -msgid "Expiration Date" -msgstr "" - -msgid "Invalid date format. Please use YYYY-MM-DD." -msgstr "" - -msgid "Agent" -msgstr "" - -msgid "Default" -msgstr "" - -msgid "Home" -msgstr "" - -msgid "Office" -msgstr "" - -msgid "Free" -msgstr "" - -msgid "Test" -msgstr "" - -msgid "One Month" -msgstr "" - -msgid "Three Months" -msgstr "" - -msgid "Six Months" -msgstr "" - -msgid "Twelve Months" -msgstr "" - -msgid "ONU Manager" -msgstr "" - -msgid "ONT" -msgstr "" - -msgid "ONT Serial Number" -msgstr "" - -msgid "Contact" -msgstr "" - -msgid "E-mail" -msgstr "" - -msgid "Telephone" -msgstr "" - -msgid "User Status" -msgstr "" - -msgid "Online User" -msgstr "" - -msgid "Up Time" -msgstr "" - -msgid "Renewal Date" -msgstr "" - -msgid "Process ID" -msgstr "" - -msgid "Forced Offline" -msgstr "" diff --git a/luci-app-pppoe-user/po/zh-cn b/luci-app-pppoe-user/po/zh-cn deleted file mode 120000 index 8d69574d..00000000 --- a/luci-app-pppoe-user/po/zh-cn +++ /dev/null @@ -1 +0,0 @@ -zh_Hans \ No newline at end of file diff --git a/luci-app-pppoe-user/po/zh_Hans/pppoe-user.po b/luci-app-pppoe-user/po/zh_Hans/pppoe-user.po deleted file mode 100644 index ba1e9ab3..00000000 --- a/luci-app-pppoe-user/po/zh_Hans/pppoe-user.po +++ /dev/null @@ -1,167 +0,0 @@ -msgid "Management" -msgstr "管理" - -msgid "User Manager" -msgstr "用户管理" - -msgid "New Users" -msgstr "新增用户" - -msgid "User List" -msgstr "用户列表" - -msgid "ONU List" -msgstr "光猫列表" - -msgid "User Number" -msgstr "用户数" - -msgid "Enabled" -msgstr "启用" - -msgid "User Name" -msgstr "用户名" - -msgid "Account" -msgstr "账号" - -msgid "This username is already in use. Please choose a different one." -msgstr "该用户名已被使用。 请选择一个不同的。" - -msgid "Service Name" -msgstr "服务名称" - -msgid "Password" -msgstr "密码" - -msgid "Password must be between 8 and 16 characters." -msgstr "密码必须介于 8 到 16 个字符之间。" - -msgid "IP Address" -msgstr "IP 地址" - -msgid "Invalid IPv4 address. Each segment must be between 0 and 255." -msgstr "IPv4 地址无效。 每个段必须介于 0 到 255 之间。" - -msgid "This IP address is already in use by another user. Please choose a different one." -msgstr "该 IP 地址已被其他用户使用。 请选择一个不同的。" - -msgid "Invalid subnet size. It must be between 0 and 32." -msgstr "子网大小无效。 它必须介于 0 和 32 之间。" - -msgid "Invalid input. Please use "*" or a valid IPv4, IPv6 address, or IP network segment." -msgstr "输入无效。 请使用“*”或有效的 IPv4、IPv6 地址或 IP 网段。" - -msgid "Package" -msgstr "套餐" - -msgid "Speed" -msgstr "网速" - -msgid "QoS" -msgstr "限速" - -msgid "Upload Speed" -msgstr "上传速度" - -msgid "Download Speed" -msgstr "下载速度" - -msgid "Speed Unit" -msgstr "速度单位" - -msgid "Connection Number" -msgstr "连接数" - -msgid "Queuing Rules" -msgstr "队列规则" - -msgid "Queue Script" -msgstr "队列脚本" - -msgid "Link Layer" -msgstr "链路层" - -msgid "Overhead Bytes" -msgstr "开销字节" - -msgid "Debug Logging" -msgstr "调试日志" - -msgid "Opening Date" -msgstr "开通日期" - -msgid "Validity Period" -msgstr "有效期" - -msgid "Expiration Date" -msgstr "截止日期" - -msgid "Invalid date format. Please use YYYY-MM-DD." -msgstr "日期格式无效。 请使用 YYYY-MM-DD。" - -msgid "Agent" -msgstr "代理人" - -msgid "Default" -msgstr "默认" - -msgid "Home" -msgstr "家" - -msgid "Office" -msgstr "办公" - -msgid "Free" -msgstr "免费" - -msgid "Test" -msgstr "测试" - -msgid "One Month" -msgstr "一个月" - -msgid "Three Months" -msgstr "三个月" - -msgid "Six Months" -msgstr "半年" - -msgid "Twelve Months" -msgstr "一年" - -msgid "ONU Manager" -msgstr "光猫管理" - -msgid "ONT" -msgstr "光猫绑定" - -msgid "ONT Serial Number" -msgstr "光猫序列号" - -msgid "Contact" -msgstr "联系方式" - -msgid "E-mail" -msgstr "电子邮箱" - -msgid "Telephone" -msgstr "电话" - -msgid "User Status" -msgstr "用户状态" - -msgid "Online User" -msgstr "在线用户" - -msgid "Up Time" -msgstr "上线时间" - -msgid "Renewal Date" -msgstr "续订日期" - -msgid "Process ID" -msgstr "进程 ID" - -msgid "Forced Offline" -msgstr "强制下线" diff --git a/luci-app-pppoe-user/root/etc/config/pppoe-user b/luci-app-pppoe-user/root/etc/config/pppoe-user deleted file mode 100644 index 490a4c70..00000000 --- a/luci-app-pppoe-user/root/etc/config/pppoe-user +++ /dev/null @@ -1,13 +0,0 @@ - -config user - option enabled '1' - option username 'guest' - option servicename '*' - option password '12345678' - option ipaddr '*' - option package 'home' - option qos '1' - option urate '15' - option drate '30' - option connect '1024' - diff --git a/luci-app-pppoe-user/root/etc/hotplug.d/iface/30-interface b/luci-app-pppoe-user/root/etc/hotplug.d/iface/30-interface deleted file mode 100644 index fe27f319..00000000 --- a/luci-app-pppoe-user/root/etc/hotplug.d/iface/30-interface +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/sh - -. /lib/functions.sh - -PPPOE_TIME="$(date "+%Y-%m-%d_%H:%M:%S")" -GATEWAY="$(uci get network."$INTERFACE".gateway)" -PING="$(ping "$GATEWAY" -I "$DEVICE" -c 5 | awk '{ print strftime("%Y-%m-%d %H:%M:%S",systime())"\t" $0; fflush() }' | grep -E 'rtt|round-trip' | cut -d "=" -f2)" - -echo "$PPPOE_TIME Track Device=$DEVICE, Interface=$INTERFACE, Gateway=$GATEWAY, Ping=$PING, Action=$ACTION" >> /var/pppoe-user/log/interface.log diff --git a/luci-app-pppoe-user/root/etc/init.d/pppoe-user b/luci-app-pppoe-user/root/etc/init.d/pppoe-user deleted file mode 100755 index 062c9637..00000000 --- a/luci-app-pppoe-user/root/etc/init.d/pppoe-user +++ /dev/null @@ -1,180 +0,0 @@ -#!/bin/sh /etc/rc.common -# Copyright (C) 2024-2026 OpenWrt.org -# Licensed under GPL v2 - -START=90 -STOP=10 - -USE_PROCD=1 -PROG=/usr/sbin/pppoe-server -UBUS_HELPER=/usr/share/ucode/pppoe-ubus.uc -UCODE_BIN=/usr/bin/ucode - -SPEED_LIMIT_SWITCH=1 -CHAP_SECRETS=/etc/ppp/chap-secrets -BACKUP_DIR=/var/pppoe-user/backup -LOG_DIR=/var/pppoe-user/log -SESSION_DIR=/var/etc/pppoe-user/session - -# 初始化目录 -init_dirs() { - mkdir -p "$BACKUP_DIR" - mkdir -p "$LOG_DIR" - mkdir -p "$LOG_DIR/userinfo" - mkdir -p "$SESSION_DIR" - mkdir -p /var/lock -} - -# 使用 ucode 进行日期比较和状态检查 (更高效且不易出错) -check_expiration_and_build_secrets() { - local current_date - current_date=$(date +"%Y-%m-%d") - - # 清空并重建 chap-secrets - : > "$CHAP_SECRETS" - - # 遍历 UCI 配置 - config_load pppoe-user - - config_foreach setup_user "$current_date" -} - -# 回调函数:处理每个用户配置 -# 参数 1: cfg_name, 参数 2: current_date -setup_user() { - local cfg="$1" - local today="$2" - local enabled username servicename password ipaddr expires - - config_get_bool enabled "$cfg" enabled 0 - config_get username "$cfg" username - config_get servicename "$cfg" servicename - config_get password "$cfg" password - config_get ipaddr "$cfg" ipaddr - config_get expires "$cfg" expires - - # 默认 servicename 为 * - [ -z "$servicename" ] && servicename="*" - - # 检查过期 - if [ "$enabled" -eq 1 ] && [ -n "$expires" ] && [ "$expires" = "$today" ]; then - logger -t pppoe-user "User $username expired today ($today). Disabling." - uci set pppoe-user."$cfg".enabled=0 - uci commit pppoe-user - enabled=0 - fi - - # 如果启用且未过期,写入 chap-secrets - if [ "$enabled" -eq 1 ]; then - # 格式: user service secret IP - echo "$username $servicename $password $ipaddr" >> "$CHAP_SECRETS" - fi -} - -# 管理 Crontab 任务 -manage_cron() { - local cron_cmd_start="59 6 * * * /etc/init.d/pppoe-user start" - local cron_cmd_backup="59 23 * * * cp -f /etc/config/pppoe-user $BACKUP_DIR/pppoe-user.\$(date +\%Y\%m\%d).bak" - - # 移除旧的任务(防止重复) - crontab -u root -l 2>/dev/null | grep -v "/etc/init.d/pppoe-user start" | grep -v "cp -f /etc/config/pppoe-user" | crontab -u root - - - # 添加新任务 - (crontab -u root -l 2>/dev/null; echo "$cron_cmd_start"; echo "$cron_cmd_backup") | sort -u | crontab -u root - -} - -# 配置 nftables 限速表 -setup_nft() { - # 检查表是否存在,存在则先删除以保证幂等性 - nft list table inet pppoe >/dev/null 2>&1 && nft delete table inet pppoe - - nft add table inet pppoe - nft add chain inet pppoe upload { type filter hook prerouting priority filter\; policy accept\; } - nft add chain inet pppoe download { type filter hook postrouting priority filter\; policy accept\; } - - # 更新防火墙区域配置以包含 ppp 接口 - local zone_device - zone_device=$(uci get firewall.@zone[0].device 2>/dev/null) - if [ -z "$(echo "$zone_device" | grep 'ppp+')" ]; then - uci add_list firewall.@zone[0].device='ppp+' - uci commit firewall - # 注意:这里不重启 firewall,以免断开现有连接,nft 规则即时生效 - fi -} - -cleanup_nft() { - nft list table inet pppoe >/dev/null 2>&1 && nft delete table inet pppoe -} - -# 启动 ucode ubus 辅助服务 -start_ubus_helper() { - if [ -x "$UCODE_BIN" ] && [ -f "$UBUS_HELPER" ]; then - # 检查是否已在运行 - if ! pgrep -f "ucode.*pppoe-ubus.uc" >/dev/null; then - logger -t pppoe-user "Starting ubus helper service" - $UCODE_BIN $UBUS_HELPER & - disown - fi - else - logger -t pppoe-user "Warning: ucode helper not found or not executable. LuCI kill function may fail." - fi -} - -stop_ubus_helper() { - pkill -f "ucode.*pppoe-ubus.uc" -} - -start_service() { - init_dirs - - logger -t pppoe-user "Starting PPPoE User Management Service" - - # 备份旧的 chap-secrets - [ -f "$CHAP_SECRETS" ] && cp -f "$CHAP_SECRETS" /etc/ppp/lasttime-chap-secrets - - # 生成新的 chap-secrets 并检查过期 - check_expiration_and_build_secrets - - # 设置权限 - chmod 600 "$CHAP_SECRETS" - - # 配置定时任务 - manage_cron - - # 配置限速 - if [ "$SPEED_LIMIT_SWITCH" = "1" ]; then - setup_nft - else - cleanup_nft - fi - - # 启动 ubus 辅助进程 (供 LuCI JS 调用) - start_ubus_helper - - # 如果是作为服务运行,不需要保持前台,因为主要工作是生成配置和规则 - # 真正的 PPPoE 服务由 pppoe-server 守护进程管理 -} - -stop_service() { - logger -t pppoe-user "Stopping PPPoE User Management Service" - - stop_ubus_helper - cleanup_nft - - # 可选:清除 chap-secrets (通常不建议,因为重启后需要,但在 stop 时清理可确保安全) - # : > "$CHAP_SECRETS" -} - -reload_service() { - stop_service - start_service -} - -# 兼容旧版 procd 的 stop 行为 -stop() { - stop_service -} - -service_triggers() { - procd_add_reload_trigger "pppoe-user" -} diff --git a/luci-app-pppoe-user/root/etc/ppp/ip-down b/luci-app-pppoe-user/root/etc/ppp/ip-down deleted file mode 100755 index 7448a641..00000000 --- a/luci-app-pppoe-user/root/etc/ppp/ip-down +++ /dev/null @@ -1,41 +0,0 @@ -#!/bin/sh - -# This script is run by the pppd _after_ the link is brought down. -# It should be used to delete routes, unset IP addresses, etc. - -# Check if the required number of arguments is provided -if [ $# -ne 5 ]; then - echo "Usage: $0 " >&2 - exit 1 -fi - -# Reset the PATH variable -PATH=/usr/sbin:/sbin:/usr/bin:/bin -export PATH - -# Log the start of the script -logger "Starting ip-down script for interface $1 with local IP $4 and peer IP $5" - -# Define the directory containing additional scripts -IP_DOWN_D_DIR="/etc/ppp/ip-down.d" - -# Check if the directory exists -if [ ! -d "$IP_DOWN_D_DIR" ]; then - echo "Directory $IP_DOWN_D_DIR does not exist." >&2 - exit 1 -fi - -# Loop through all files in the ip-down.d directory and execute them if they are executable -for script in "$IP_DOWN_D_DIR"/*; do - if [ -x "$script" ]; then - "$script" "$@" || { - echo "Error executing $script" >&2 - exit 1 - } - fi -done - -# Log the successful completion of the script -logger "Completed ip-down script for interface $1" - -exit 0 diff --git a/luci-app-pppoe-user/root/etc/ppp/ip-down.d/offline-users b/luci-app-pppoe-user/root/etc/ppp/ip-down.d/offline-users deleted file mode 100755 index 622c09d1..00000000 --- a/luci-app-pppoe-user/root/etc/ppp/ip-down.d/offline-users +++ /dev/null @@ -1,58 +0,0 @@ -#!/bin/sh -# -# This script is run by pppd _after_ the link is brought down. -# It should be used to delete routes, unset IP addresses, etc. -# -# This script is called with the following arguments: -# Arg Name Example -# $1 Interface name ppp0 -# $2 The tty ttyS1 -# $3 The link speed 38400 -# $4 Local IP number 12.34.56.78 -# $5 Peer IP number 12.34.56.99 -# - -# Reset the PATH environment variable -PATH=/usr/sbin:/sbin:/usr/bin:/bin -export PATH - -# Get the current date and time -QUIT_TIME="$(date "+%Y-%m-%d_%H:%M:%S")" - -# Extract username, interface name, and peer IP address from arguments -USERNAME="${PEERNAME}" -IFACE="${1}" -TTY="${2}" -PEERIP="${5}" - -# Paths and Configuration -USER_LOG="/var/pppoe-user/log/userinfo" -USER_LOG_FILE="${USER_LOG}/${USERNAME}.log" -USER_CONFIG="pppoe-user" -USER_CFGID="$(uci show ${USER_CONFIG} | grep "${USERNAME}" | cut -d '.' -sf 2)" -USER_CONNECT="$(uci get ${USER_CONFIG}.${USER_CFGID}.connect)" -SESSION_PATH="/var/etc/pppoe-user/session" - -# Remove the session file -rm -f "${SESSION_PATH}/${USERNAME}.${IFACE}" - -# Log the disconnection details -echo "${QUIT_TIME} ${USERNAME} Offline Interface: ${IFACE} IP: ${PEERIP}" >> "${USER_LOG_FILE}" -echo "Connect time: ${CONNECT_TIME} minutes" >> "${USER_LOG_FILE}" -echo "Sent: ${BYTES_SENT} bytes" >> "${USER_LOG_FILE}" -echo "Received: ${BYTES_RCVD} bytes" >> "${USER_LOG_FILE}" - -# Calculate and log the sum and average speed -sum_bytes=$((${BYTES_SENT} + ${BYTES_RCVD})) -sum=$(echo "scale=2;${sum_bytes}/1024/1024" | bc) -echo "Bytes sum: ${sum} MB" >> "${USER_LOG_FILE}" -ave=$(echo "scale=2;${sum_bytes}/1024/${CONNECT_TIME}" | bc) -echo "Average speed: ${ave} KB/s" >> "${USER_LOG_FILE}" - -# Add an empty line for separation in the log file -echo >> "${USER_LOG_FILE}" - -# Log the execution -logger "${QUIT_TIME} ${PEERNAME} ${PEERIP} OFFLINE-USERS >>>>>> The script has been executed!" - -exit 0 diff --git a/luci-app-pppoe-user/root/etc/ppp/ip-down.d/speed-limit b/luci-app-pppoe-user/root/etc/ppp/ip-down.d/speed-limit deleted file mode 100755 index 9fe4e931..00000000 --- a/luci-app-pppoe-user/root/etc/ppp/ip-down.d/speed-limit +++ /dev/null @@ -1,39 +0,0 @@ -#!/bin/sh -( - flock -n 200 || exit 1 - - # The environment is cleared before executing this script - PATH=/usr/sbin:/sbin:/usr/bin:/bin - export PATH - - QUIT_TIME="$(date "+%Y-%m-%d_%H:%M:%S")" - USERNAME="${PEERNAME}" - PEERIP="${5}" - - USER_CONFIG="pppoe-user" - USER_CFGID="$(uci show ${USER_CONFIG} | grep "${USERNAME}" | cut -d '.' -f 2)" - TABLE="pppoe" - - # Define chains - CHAINS="upload download" - - # Loop through each chain and delete all rules matching the IP - for CHAIN in ${CHAINS}; do - # Get all rule handles associated with the IP in the current chain - RULE_HANDLES="$(nft -a list chain inet ${TABLE} ${CHAIN} | grep "${PEERIP}" | awk '{print $NF}')" - - if [ -n "${RULE_HANDLES}" ]; then - for HANDLE in ${RULE_HANDLES}; do - nft delete rule inet ${TABLE} ${CHAIN} handle "${HANDLE}" || { - logger "${QUIT_TIME} ERROR: Failed to delete rule handle ${HANDLE} in chain ${CHAIN} for ${USERNAME} ${PEERIP}" - exit 1 - } - done - else - logger "${QUIT_TIME} WARNING: No rules found for ${USERNAME} ${PEERIP} in chain ${CHAIN}" - fi - done - - exit 0 - -) 200>/var/lock/ipdown.lock diff --git a/luci-app-pppoe-user/root/etc/ppp/ip-up b/luci-app-pppoe-user/root/etc/ppp/ip-up deleted file mode 100755 index b5302cb5..00000000 --- a/luci-app-pppoe-user/root/etc/ppp/ip-up +++ /dev/null @@ -1,41 +0,0 @@ -#!/bin/sh - -# This script is run by the pppd after the link is established. -# It should be used to add routes, set IP address, run the mailq, etc. - -# Check if the required number of arguments is provided -if [ $# -ne 5 ]; then - echo "Usage: $0 " >&2 - exit 1 -fi - -# Reset the PATH variable -PATH=/usr/sbin:/sbin:/usr/bin:/bin -export PATH - -# Log the start of the script -logger "Starting ip-up script for interface $1 with local IP $4 and peer IP $5" - -# Define the directory containing additional scripts -IP_UP_D_DIR="/etc/ppp/ip-up.d" - -# Check if the directory exists -if [ ! -d "$IP_UP_D_DIR" ]; then - echo "Directory $IP_UP_D_DIR does not exist." >&2 - exit 1 -fi - -# Loop through all files in the ip-up.d directory and execute them if they are executable -for script in "$IP_UP_D_DIR"/*; do - if [ -x "$script" ]; then - "$script" "$@" || { - echo "Error executing $script" >&2 - exit 1 - } - fi -done - -# Log the successful completion of the script -logger "Completed ip-up script for interface $1" - -exit 0 diff --git a/luci-app-pppoe-user/root/etc/ppp/ip-up.d/online-users b/luci-app-pppoe-user/root/etc/ppp/ip-up.d/online-users deleted file mode 100755 index 41ef74eb..00000000 --- a/luci-app-pppoe-user/root/etc/ppp/ip-up.d/online-users +++ /dev/null @@ -1,87 +0,0 @@ -#!/bin/sh -# -# This script is run by pppd after the link is established. -# It is used to add routes, set IP address, run mailq, etc. -# -# This script is called with the following arguments: -# Arg Name Example -# $1 Interface name ppp0 -# $2 The tty ttyS1 -# $3 The link speed 38400 -# $4 Local IP number 12.34.56.78 -# $5 Peer IP number 12.34.56.99 -# - -# Reset the PATH environment variable -PATH=/usr/sbin:/sbin:/usr/bin:/bin -export PATH - -LOGIN_TIME=$(date "+%Y-%m-%d_%H:%M:%S") -USERNAME="${PEERNAME}" -IFACE="${1}" -TTY="${2}" -PEERIP="${5}" - -USER_LOG="/var/pppoe-user/log/userinfo" -USER_LOG_FILE="${USER_LOG}/${USERNAME}.log" -USER_CONFIG="pppoe-user" -USER_CFGID="$(uci show ${USER_CONFIG} | grep "${USERNAME}" | cut -d '.' -sf 2)" -SERVIC_NAME="$(uci get ${USER_CONFIG}.${USER_CFGID}.servicename)" -USER_PACKAGE="$(uci get ${USER_CONFIG}.${USER_CFGID}.package)" -USER_QOS="$(uci get ${USER_CONFIG}.${USER_CFGID}.qos)" -USER_UPLOAD="$(uci get ${USER_CONFIG}.${USER_CFGID}.urate)" -USER_DOWNLOAD="$(uci get ${USER_CONFIG}.${USER_CFGID}.drate)" -USER_CONNECT="$(uci get ${USER_CONFIG}.${USER_CFGID}.connect)" -RENEWAL_DATE="$(uci get ${USER_CONFIG}.${USER_CFGID}.expires)" - -CONFIG_PATH="/var/etc/pppoe-user" -SESSION_PATH="${CONFIG_PATH}/session" - -PID="$(cat /var/run/pppd/${IFACE}.pid 2>/dev/null)" -MAC="$(ps -w | grep pppd | grep -v 'grep' | grep -h -m 1 "${PID}" | awk '{print $10}' | cut -d : -f 2,2-7)" - -mkdir -p "${USER_LOG}" -mkdir -p "${SESSION_PATH}" - -# Write session information in JSON format -cat <<-EOF > "${SESSION_PATH}/${USERNAME}.${IFACE}" -{ - "username": "${USERNAME}", - "mac": "${MAC}", - "interface": "${IFACE}", - "tty": "${TTY}", - "speed": "${SPEED}", - "ip": "${PEERIP}", - "servicename": "${SERVIC_NAME}", - "package": "${USER_PACKAGE}", - "connect": "${USER_CONNECT}", - "updated": "${RENEWAL_DATE}", - "uptime": "${LOGIN_TIME}", - "pid": "${PID}" -} -EOF - -# Find and kill previous PPP sessions for the same user -cfgid=$(uci show ${USER_CONFIG} | grep "@user" | grep ".username='${USERNAME}'" | cut -d '.' -sf 2) -if [ -n "$cfgid" ]; then - HAS_LOGIN=$(ls "${SESSION_PATH}" | grep "^${USERNAME}\.ppp" | grep -v "${IFACE}") - if [ -n "$HAS_LOGIN" ]; then - KO_IFACE=$(echo "$HAS_LOGIN" | awk -F '.' '{print $2}') - KO_PID=$(cat "/var/run/${KO_IFACE}.pid" 2>/dev/null) - [ -n "$KO_PID" ] && kill -9 "$KO_PID" >/dev/null 2>&1 - rm -f "${SESSION_PATH}/${HAS_LOGIN}" - rm -f "/var/run/${KO_IFACE}.pid" - fi -fi - -# Update the MAC address in the user's configuration -uci set "${USER_CONFIG}.${USER_CFGID}.macaddr=${MAC}" -uci commit "${USER_CONFIG}" - -# Log the connection details -echo "${LOGIN_TIME} ${USERNAME} Online PID: ${PID} Interface: ${IFACE} IP: ${PEERIP} MAC: ${MAC}" >> "${USER_LOG_FILE}" -echo >> "${USER_LOG_FILE}" - -logger "${LOGIN_TIME} ${PEERNAME} ${PEERIP} ONLINE-USERS >>>>>> The script has been executed!" - -exit 0 diff --git a/luci-app-pppoe-user/root/etc/ppp/ip-up.d/speed-limit b/luci-app-pppoe-user/root/etc/ppp/ip-up.d/speed-limit deleted file mode 100755 index 7bb26c96..00000000 --- a/luci-app-pppoe-user/root/etc/ppp/ip-up.d/speed-limit +++ /dev/null @@ -1,58 +0,0 @@ -#!/bin/sh -( - flock -n 200 || exit 1 - - # Environment variable settings - PATH=/usr/sbin:/sbin:/usr/bin:/bin - export PATH - - LOGIN_TIME="$(date "+%Y-%m-%d_%H:%M:%S")" - USERNAME="${PEERNAME}" - PEERIP="${5}" - - USER_CONFIG="pppoe-user" - USER_CFGID="$(uci show ${USER_CONFIG} | grep "${USERNAME}" | cut -d '.' -f 2)" - - # Convert Mbps to KBytes - USER_QOS="$(uci get ${USER_CONFIG}.${USER_CFGID}.qos 2>/dev/null)" - USER_UPLOAD="$(($(uci get ${USER_CONFIG}.${USER_CFGID}.urate 2>/dev/null) * 125))" - USER_DOWNLOAD="$(($(uci get ${USER_CONFIG}.${USER_CFGID}.drate 2>/dev/null) * 125))" - UPLOAD_BURST="$((USER_UPLOAD / 10))" - DOWNLOAD_BURST="$((USER_DOWNLOAD / 10))" - USER_CONNECT="$(uci get ${USER_CONFIG}.${USER_CFGID}.connect 2>/dev/null)" - - # Ensure that the obtained values are valid - if [ -z "${USER_QOS}" ] || [ -z "${USER_UPLOAD}" ] || [ -z "${USER_DOWNLOAD}" ] || [ -z "${USER_CONNECT}" ]; then - logger "${LOGIN_TIME} ERROR: Failed to get user config for (${USERNAME}) (${PEERIP})" - exit 1 - fi - - UP="upload" - DOWN="download" - - # Add traffic control rules - if [ "${USER_QOS}" = "1" ]; then - # Ensure rules are inserted in the correct order - - # Upload chain - nft insert rule inet pppoe ${UP} ip saddr "${PEERIP}" ct count over "${USER_CONNECT}" drop - nft insert rule inet pppoe ${UP} ip saddr "${PEERIP}" ct state new limit rate "${USER_CONNECT}"/minute accept - nft insert rule inet pppoe ${UP} ip saddr "${PEERIP}" limit rate over "${USER_UPLOAD}" kbytes/second burst "${UPLOAD_BURST}" kbytes counter drop - if [ $? -ne 0 ]; then - logger "${LOGIN_TIME} ERROR: Failed to insert upload rules (${USERNAME}) (${PEERIP})" - exit 1 - fi - - # Download chain - nft insert rule inet pppoe ${DOWN} ip daddr "${PEERIP}" ct count over "${USER_CONNECT}" drop - nft insert rule inet pppoe ${DOWN} ip daddr "${PEERIP}" ct state new limit rate "${USER_CONNECT}"/minute accept - nft insert rule inet pppoe ${DOWN} ip daddr "${PEERIP}" limit rate over "${USER_DOWNLOAD}" kbytes/second burst "${DOWNLOAD_BURST}" kbytes counter drop - if [ $? -ne 0 ]; then - logger "${LOGIN_TIME} ERROR: Failed to insert download rules (${USERNAME}) (${PEERIP})" - exit 1 - fi - fi - - exit 0 - -) 200>/var/lock/ipup.lock diff --git a/luci-app-pppoe-user/root/etc/uci-defaults/pppoe-user b/luci-app-pppoe-user/root/etc/uci-defaults/pppoe-user deleted file mode 100644 index a62bf45e..00000000 --- a/luci-app-pppoe-user/root/etc/uci-defaults/pppoe-user +++ /dev/null @@ -1,25 +0,0 @@ -#!/bin/sh -[ ! -f "/usr/share/ucitrack/luci-app-pppoe-user.json" ] && { - cat > /usr/share/ucitrack/luci-app-pppoe-user.json << EEOF -{ - "config": "pppoe-user", - "init": "pppoe-user" -} -EEOF -} - -uci -q batch <<-EOF >/dev/null - delete ucitrack.@pppoe-user[-1] - add ucitrack pppoe-user - set ucitrack.@pppoe-user[-1].init=pppoe-user - commit ucitrack -EOF - -chmod 600 /etc/config/pppoe-user -chmod 755 /etc/init.d/pppoe-user -chmod 755 /etc/ppp/ip-up /etc/ppp/ip-down -chmod 755 /etc/ppp/auth-up /etc/ppp/auth-down -chmod +x /etc/ppp/ip-up.d/* /etc/ppp/ip-down.d/* - -rm -rf /tmp/luci-*cache -exit 0 diff --git a/luci-app-pppoe-user/root/usr/share/luci/menu.d/luci-app-pppoe-user.json b/luci-app-pppoe-user/root/usr/share/luci/menu.d/luci-app-pppoe-user.json deleted file mode 100644 index e0ef4e49..00000000 --- a/luci-app-pppoe-user/root/usr/share/luci/menu.d/luci-app-pppoe-user.json +++ /dev/null @@ -1,71 +0,0 @@ -{ - "admin/pppoe-user": { - "title": "Management", - "order": 60, - "action": { - "type": "alias", - }, - "depends": { - "acl": [ "luci-app-pppoe-user" ], - } - }, - - "admin/pppoe-user/usermanager": { - "title": "User Manager", - "order": 1, - "action": { - "type": "view", - "path": "pppoe-user/user", - } - }, - - "admin/pppoe-user/onumanager": { - "title": "ONU Manager", - "order": 2, - "action": { - "type": "view", - "path": "pppoe-user/onu", - } - }, - - "admin/status/userstatus": { - "title": "User Status", - "order": 88, - "action": { - "type": "alias", - "path": "admin/status/userstatus/onlineuser", - "post": { "cbi.submit": true } - }, - "depends": { - "acl": [ "luci-app-pppoe-user" ], - "uci": { "pppoe-user": true } - } - }, - - "admin/status/userstatus/onlineuser": { - "title": "Online User", - "order": 1, - "action": { - "type": "form", - "path": "pppoe-user/onlineuser", - } - }, - - "admin/status/userstatus/downtimeuser": { - "title": "Downtime User", - "order": 2, - "action": { - "type": "form", - "path": "pppoe-user/downtimeuser", - } - }, - - "admin/status/userstatus/interface": { - "title": "Interface Monitor", - "order": 3, - "action": { - "type": "form", - "path": "pppoe-user/interface", - } - } -} diff --git a/luci-app-pppoe-user/root/usr/share/rpcd/acl.d/luci-app-pppoe-user.json b/luci-app-pppoe-user/root/usr/share/rpcd/acl.d/luci-app-pppoe-user.json deleted file mode 100644 index 2940bba9..00000000 --- a/luci-app-pppoe-user/root/usr/share/rpcd/acl.d/luci-app-pppoe-user.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "luci-app-pppoe-user": { - "description": "Grant access to pppoe-user", - "read": { - "uci": [ "pppoe-user" ], - "file": { - "/var/etc/pppoe-user/session/*.json": [ "read" ], - "/var/pppoe-user/log/interface.log": [ "read" ], - "/proc/net/dev": [ "read" ] - } - }, - "write": { - "uci": [ "pppoe-user" ], - "exec": { - "/bin/kill": [ "arg1" ], - "/usr/bin/unlink": [ "arg1" ] - } - } - } -} diff --git a/luci-app-pppoe-user/root/usr/share/rpcd/ucode/pppoe-ubus.uc b/luci-app-pppoe-user/root/usr/share/rpcd/ucode/pppoe-ubus.uc deleted file mode 100644 index 351dad80..00000000 --- a/luci-app-pppoe-user/root/usr/share/rpcd/ucode/pppoe-ubus.uc +++ /dev/null @@ -1,29 +0,0 @@ -#!/usr/bin/ucode -import { ubus } from 'ubus'; -import { unlink } from 'fs'; - -let conn = ubus.connect(); -if (!conn) exit(1); - -conn.add({ - name: 'pppoe.user', - methods: { - kill: { - args: { session_file: '', pid: '' }, - call: function(req, args) { - if (!args.session_file || !args.pid) - return { error: 'Invalid arguments' }; - - try { - unlink(args.session_file); - system(`kill -15 ${args.pid}`); - return { success: true }; - } catch (e) { - return { error: e }; - } - } - } - } -}); - -conn.listen(); diff --git a/luci-app-pppoe-user/root/usr/share/rpcd/ucode/pppoe-user.uc b/luci-app-pppoe-user/root/usr/share/rpcd/ucode/pppoe-user.uc deleted file mode 100644 index 81530485..00000000 --- a/luci-app-pppoe-user/root/usr/share/rpcd/ucode/pppoe-user.uc +++ /dev/null @@ -1,107 +0,0 @@ -#!/usr/bin/ucode -/* /usr/share/ucode/pppoe-user.uc */ - -import { readfile, listdir, stat } from 'fs'; -import { exec } from 'ubus'; -import { parse } from 'jsonc'; - -const SESSION_PATH = '/var/etc/pppoe-user/session'; -const LOG_PATH = '/var/pppoe-user/log/interface.log'; - -// 获取在线用户列表 -export function get_online_users() { - let users = []; - let count = 0; - - // 统计 ppp 接口数量 (模拟原 lua 逻辑) - try { - let dev = readfile('/proc/net/dev'); - if (dev) { - for (let line in split(dev, /\n/)) { - if (match(line, /^\s*ppp/)) count++; - } - } - } catch (e) {} - - if (!stat(SESSION_PATH)) return { count: count, sessions: [] }; - - let files = listdir(SESSION_PATH); - if (!files) return { count: count, sessions: [] }; - - for (let filename in files) { - let filepath = `${SESSION_PATH}/${filename}`; - if (!stat(filepath)?.is_regular) continue; - - try { - let content = readfile(filepath); - if (content) { - let data = parse(content); - if (data) { - data.session_file = filepath; - push(users, data); - } - } - } catch (e) { - warn(`Failed to read ${filepath}: ${e}`); - } - } - - return { count: count, sessions: users }; -} - -// 强制下线用户 -export function kill_user(session_file, pid) { - if (!session_file || !pid) return { error: 'Invalid parameters' }; - - try { - // 删除会话文件 - unlink(session_file); - - // 杀死进程 - let ret = system(`kill -15 ${pid}`); - - return { success: true }; - } catch (e) { - return { error: e }; - } -} - -// 获取接口日志 -export function get_interface_log() { - if (!stat(LOG_PATH)) return ''; - - try { - // 读取最后 200 行 (简单实现,ucode 没有直接的 tail,需读取后分割) - let content = readfile(LOG_PATH); - if (!content) return ''; - - let lines = split(content, /\n/); - let tail_lines = slice(lines, -200); - // 反转以符合原逻辑 (最新在上) - return join(reverse(tail_lines), '\n'); - } catch (e) { - return `Error reading log: ${e}`; - } -} - -// 获取下线用户统计 (简单模拟,实际需解析 UCI) -export function get_downtime_count() { - // 这里可以通过 ubus 调用 uci 或者直接解析 /etc/config/pppoe-user - // 为简化,建议在 JS 层直接处理 UCI 数据,或者在此处调用 uci get - try { - let res = exec('uci', { 'command': 'show', 'params': ['pppoe-user'] }); - if (res && res.output) { - let lines = split(res.output, /\n/); - let cnt = 0; - for (let line in lines) { - if (match(line, /=enabled$/)) { // 粗略匹配,更严谨需解析 value - // 实际上 ucidef 或 uci get 更好,这里仅作演示逻辑占位 - // 建议在前端 JS 直接遍历 uci.load('pppoe-user') - } - } - // 由于 ucode 直接调 uci 命令解析较繁琐,推荐此逻辑移至前端 JS 处理 UCI 对象 - return 0; - } - } catch (e) {} - return 0; -} diff --git a/luci-app-pppoe-user/root/usr/share/ucitrack/luci-app-pppoe-user.json b/luci-app-pppoe-user/root/usr/share/ucitrack/luci-app-pppoe-user.json deleted file mode 100644 index 3ed4d5ef..00000000 --- a/luci-app-pppoe-user/root/usr/share/ucitrack/luci-app-pppoe-user.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "config": "pppoe-user", - "init": "pppoe-user" -} diff --git a/luci-app-pppoe-user/screenshots/001.jpg b/luci-app-pppoe-user/screenshots/001.jpg deleted file mode 100644 index 9f336272..00000000 Binary files a/luci-app-pppoe-user/screenshots/001.jpg and /dev/null differ diff --git a/luci-app-pppoe-user/screenshots/002.jpg b/luci-app-pppoe-user/screenshots/002.jpg deleted file mode 100644 index c1a01b16..00000000 Binary files a/luci-app-pppoe-user/screenshots/002.jpg and /dev/null differ diff --git a/luci-app-pppoe-user/screenshots/003.jpg b/luci-app-pppoe-user/screenshots/003.jpg deleted file mode 100644 index 9976fe45..00000000 Binary files a/luci-app-pppoe-user/screenshots/003.jpg and /dev/null differ diff --git a/luci-app-samba4/htdocs/luci-static/resources/view/samba4.js b/luci-app-samba4/htdocs/luci-static/resources/view/samba4.js index a722d616..57ad46da 100644 --- a/luci-app-samba4/htdocs/luci-static/resources/view/samba4.js +++ b/luci-app-samba4/htdocs/luci-static/resources/view/samba4.js @@ -54,6 +54,11 @@ return view.extend({ o = s.taboption('general', form.Value, 'description', _('Description')); o.placeholder = 'Samba4 on OpenWrt'; + o = s.taboption('general', form.Flag, 'autoshare', _('Auto Share'), + _('Auto share local disk which connected')); + o.default = 'yes'; + o.rmempty = false; + s.taboption('general', form.Flag, 'enable_extra_tuning', _('Enable extra Tuning'), _('Enable some community driven tuning parameters, that may improve write speeds and better operation via WiFi.\ Not recommend if multiple clients write to the same files, at the same time!')); diff --git a/pcat-manager/Makefile b/pcat-manager/Makefile index 321ccffd..078d0883 100644 --- a/pcat-manager/Makefile +++ b/pcat-manager/Makefile @@ -1,12 +1,12 @@ include $(TOPDIR)/rules.mk PKG_NAME:=pcat-manager -PKG_RELEASE:=1 +PKG_RELEASE:=2 PKG_SOURCE_PROTO:=git PKG_SOURCE_URL:=https://github.com/photonicat/rockchip_rk3568_pcat_manager.git PKG_SOURCE_DATE:=2023-11-13 -PKG_SOURCE_VERSION:=7f05f163ac864ce323ad648a4c03047915fce496 +PKG_SOURCE_VERSION:=961edb8b5cbae63efb03ee774d198f6fdb682a45 PKG_MIRROR_HASH:=skip PKG_LICENSE:=GPL-3.0-only