mirror of
https://github.com/kiddin9/op-packages.git
synced 2026-07-27 02:11:19 +08:00
⛅ Sync 2026-03-22 20:18:41
This commit is contained in:
parent
8d097e3078
commit
a660863dba
@ -1,13 +1,19 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=luci-app-guest-wifi
|
||||
PKG_MAINTAINER:=kenzok8 <https://github.com/kenzok78>
|
||||
PKG_VERSION:=2.0.1
|
||||
PKG_RELEASE:=1
|
||||
PKG_RELEASE:=2
|
||||
|
||||
LUCI_TITLE:=LuCI support for guest-wifi
|
||||
LUCI_DEPENDS:=+luci-base +luci-compat
|
||||
LUCI_PKGARCH:=all
|
||||
LUCI_DESCRIPTION:=Simple guest WiFi configuration interface for LuCI
|
||||
|
||||
define Package/$(PKG_NAME)/conffiles
|
||||
/etc/config/wireless
|
||||
endef
|
||||
|
||||
include $(TOPDIR)/feeds/luci/luci.mk
|
||||
|
||||
# call BuildPackage - OpenWrt buildroot signature
|
||||
# call BuildPackage - OpenWrt buildroot signature
|
||||
|
||||
@ -1 +1,77 @@
|
||||
# luci-app-guest-wifi
|
||||
# luci-app-guest-wifi
|
||||
|
||||
适用于 OpenWrt/LuCI 的访客 WiFi 配置插件。
|
||||
|
||||
## 功能特性
|
||||
|
||||
- **访客网络配置**: 通过 Web 界面配置独立的访客无线网络
|
||||
- **加密选项**: 支持无加密、WPA2-PSK、WPA3-SAE、WPA2/WPA3 混合模式
|
||||
- **AP 隔离**: 可选的客户端隔离功能,防止访客设备之间互相访问
|
||||
- **自动生效**: 保存配置后自动重新加载无线设置
|
||||
|
||||
## 依赖
|
||||
|
||||
- `luci`
|
||||
- `luci-base`
|
||||
- `luci-compat` (用于兼容旧版 LuCI CBI 模型)
|
||||
|
||||
## 安装
|
||||
|
||||
```bash
|
||||
# 添加 feed
|
||||
echo 'src-git guestwifi https://github.com/kenzok78/luci-app-guest-wifi' >> feeds.conf.default
|
||||
./scripts/feeds update -a
|
||||
./scripts/feeds install -a -p guestwifi
|
||||
|
||||
# 编译
|
||||
make package/luci-app-guest-wifi/compile V=s
|
||||
```
|
||||
|
||||
## 使用说明
|
||||
|
||||
1. 访问 OpenWrt Web 管理界面 → 网络 → 访客WiFi
|
||||
2. 添加访客 WiFi 配置段(如果没有自动创建)
|
||||
3. 启用访客网络,设置 SSID 和加密方式
|
||||
4. 可选启用 AP 隔离功能
|
||||
5. 保存并应用
|
||||
|
||||
## 配置说明
|
||||
|
||||
| 选项 | 说明 |
|
||||
|------|------|
|
||||
| 启用 | 是否启用访客 WiFi |
|
||||
| 网络名称(SSID) | 无线网络名称 |
|
||||
| 加密方式 | 无/WPA2-PSK/WPA3-SAE/WPA2/WPA3混合 |
|
||||
| 密码 | 无线连接密码(加密模式下必填)|
|
||||
| AP 隔离 | 防止客户端之间互相通信 |
|
||||
|
||||
## 目录结构
|
||||
|
||||
```
|
||||
luci-app-guest-wifi/
|
||||
├── Makefile
|
||||
├── luasrc/
|
||||
│ ├── controller/ # LuCI 控制器
|
||||
│ └── model/cbi/ # CBI 模型 (兼容旧版)
|
||||
├── htdocs/luci-static/resources/view/
|
||||
│ └── guest-wifi/wifi.js # JavaScript 视图 (现代 LuCI)
|
||||
├── po/ # 翻译文件
|
||||
└── root/
|
||||
├── etc/
|
||||
│ ├── config/ # UCI 配置 (通过 LuCI 管理)
|
||||
│ ├── init.d/ # 启动脚本
|
||||
│ └── uci-defaults/ # 初始化脚本
|
||||
└── usr/share/
|
||||
├── luci/menu.d/ # 菜单配置
|
||||
└── rpcd/acl.d/ # ACL 权限
|
||||
```
|
||||
|
||||
## 工作原理
|
||||
|
||||
- 插件通过 UCI 配置管理 `/etc/config/wireless` 中的 `wifi-iface` 段落
|
||||
- 通过在 `wifi-iface` 配置中添加 `option guest_wifi '1'` 来标识访客网络
|
||||
- 控制器和视图会自动过滤显示所有标记为访客的 WiFi 配置段
|
||||
|
||||
## 许可证
|
||||
|
||||
Apache-2.0
|
||||
|
||||
@ -1,46 +0,0 @@
|
||||
'use strict';
|
||||
'require form';
|
||||
'require view';
|
||||
'require uci';
|
||||
'require ui';
|
||||
|
||||
return view.extend({
|
||||
load: function() {
|
||||
return Promise.all([
|
||||
uci.load('wireless')
|
||||
]);
|
||||
},
|
||||
|
||||
render: function() {
|
||||
const m = new form.Map('wireless', _('Guest WiFi'),
|
||||
_('Guest WiFi provides a separate wireless network for guest access with isolated permissions.'));
|
||||
|
||||
const s = m.section(form.TypedSection, 'wifi-iface', _('Guest WiFi Settings'));
|
||||
s.anonymous = true;
|
||||
s.addremove = true;
|
||||
|
||||
s.option(form.Flag, 'guest_wifi', _('Enable'),
|
||||
_('Enable guest WiFi network'));
|
||||
|
||||
s.option(form.Value, 'ssid', _('Network Name (SSID)'));
|
||||
|
||||
const o = s.option(form.ListValue, 'encryption', _('Encryption'));
|
||||
o.value('none', _('No Encryption'));
|
||||
o.value('psk2', _('WPA2-PSK'));
|
||||
o.value('sae', _('WPA3-SAE'));
|
||||
o.value('sae-mixed', _('WPA2/WPA3-Mixed'));
|
||||
o.default = 'psk2';
|
||||
|
||||
const key = s.option(form.Value, 'key', _('Password'));
|
||||
key.depends('encryption', 'psk2');
|
||||
key.depends('encryption', 'sae');
|
||||
key.depends('encryption', 'sae-mixed');
|
||||
key.datatype = 'wpakey';
|
||||
key.password = true;
|
||||
|
||||
s.option(form.Flag, 'isolate', _('AP Isolation'),
|
||||
_('Prevents wireless clients from communicating with each other'));
|
||||
|
||||
return m.render();
|
||||
}
|
||||
});
|
||||
@ -2,57 +2,65 @@
|
||||
'require form';
|
||||
'require view';
|
||||
'require uci';
|
||||
'require ui';
|
||||
|
||||
return view.extend({
|
||||
load: function() {
|
||||
return Promise.all([
|
||||
uci.load('wireless')
|
||||
]);
|
||||
},
|
||||
load: function() {
|
||||
return uci.load('wireless');
|
||||
},
|
||||
|
||||
render: function() {
|
||||
const m = new form.Map('wireless', _('Guest WiFi'),
|
||||
_('Guest WiFi provides a separate wireless network for guest access with isolated permissions.'));
|
||||
render: function() {
|
||||
var m = new form.Map('wireless', _('Guest WiFi'),
|
||||
_('Guest WiFi provides a separate wireless network for guest access with isolated permissions.'));
|
||||
|
||||
const s = m.section(form.TypedSection, 'wifi-iface', _('Guest WiFi Settings'));
|
||||
s.anonymous = true;
|
||||
s.addremove = true;
|
||||
s.filter = function(section_id) {
|
||||
return uci.get('wireless', section_id, 'guest_wifi') === '1';
|
||||
};
|
||||
var s = m.section(form.TypedSection, 'wifi-iface', _('Guest WiFi Settings'));
|
||||
s.anonymous = true;
|
||||
s.addremove = true;
|
||||
s.filter = function(section_id) {
|
||||
return uci.get('wireless', section_id, 'guest_wifi') === '1';
|
||||
};
|
||||
s.cfgvalue = function(section_id) {
|
||||
return uci.get('wireless', section_id, 'guest_wifi');
|
||||
};
|
||||
s.handleApply = function(section, ev) {
|
||||
uci.save(function() {
|
||||
uci.apply(function() {
|
||||
uci.load('wireless');
|
||||
XHR.poll(2);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
let o;
|
||||
var o = null;
|
||||
|
||||
o = s.option(form.Flag, 'guest_wifi', _('Enable'),
|
||||
_('Enable guest WiFi network'));
|
||||
o.rmempty = false;
|
||||
o.default = '0';
|
||||
o = s.option(form.Flag, 'guest_wifi', _('Enable'),
|
||||
_('Enable guest WiFi network'));
|
||||
o.rmempty = false;
|
||||
o.default = '0';
|
||||
|
||||
o = s.option(form.Value, 'ssid', _('Network Name (SSID)'));
|
||||
o.rmempty = false;
|
||||
o = s.option(form.Value, 'ssid', _('Network Name (SSID)'));
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.option(form.ListValue, 'encryption', _('Encryption'));
|
||||
o.value('none', _('No Encryption'));
|
||||
o.value('psk2', _('WPA2-PSK'));
|
||||
o.value('sae', _('WPA3-SAE'));
|
||||
o.value('sae-mixed', _('WPA2/WPA3-Mixed'));
|
||||
o.rmempty = false;
|
||||
o.default = 'psk2';
|
||||
o = s.option(form.ListValue, 'encryption', _('Encryption'));
|
||||
o.value('none', _('No Encryption'));
|
||||
o.value('psk2', _('WPA2-PSK'));
|
||||
o.value('sae', _('WPA3-SAE'));
|
||||
o.value('sae-mixed', _('WPA2/WPA3-Mixed'));
|
||||
o.rmempty = false;
|
||||
o.default = 'psk2';
|
||||
|
||||
o = s.option(form.Value, 'key', _('Password'));
|
||||
o.depends('encryption', 'psk2');
|
||||
o.depends('encryption', 'sae');
|
||||
o.depends('encryption', 'sae-mixed');
|
||||
o.datatype = 'wpakey';
|
||||
o.rmempty = false;
|
||||
o.password = true;
|
||||
o = s.option(form.Value, 'key', _('Password'));
|
||||
o.depends('encryption', 'psk2');
|
||||
o.depends('encryption', 'sae');
|
||||
o.depends('encryption', 'sae-mixed');
|
||||
o.datatype = 'wpakey';
|
||||
o.rmempty = false;
|
||||
o.password = true;
|
||||
|
||||
o = s.option(form.Flag, 'isolate', _('AP Isolation'),
|
||||
_('Prevents wireless clients from communicating with each other'));
|
||||
o.rmempty = false;
|
||||
o.default = '1';
|
||||
o = s.option(form.Flag, 'isolate', _('AP Isolation'),
|
||||
_('Prevents wireless clients from communicating with each other'));
|
||||
o.rmempty = false;
|
||||
o.default = '1';
|
||||
|
||||
return m.render();
|
||||
}
|
||||
});
|
||||
return m.render();
|
||||
}
|
||||
});
|
||||
|
||||
@ -1,9 +1,11 @@
|
||||
module("luci.controller.guest-wifi", package.seeall)
|
||||
|
||||
function index()
|
||||
if not nixio.fs.access("/etc/config/wireless") then
|
||||
return
|
||||
end
|
||||
local fs = require "nixio.fs"
|
||||
|
||||
entry({"admin", "network", "guest-wifi"}, view("guest-wifi/wifi"), _("Guest WiFi"), 60)
|
||||
end
|
||||
function index()
|
||||
if not fs.access("/etc/config/wireless") then
|
||||
return
|
||||
end
|
||||
|
||||
entry({"admin", "network", "guest-wifi"}, view("guest-wifi/wifi"), _("Guest WiFi"), 60)
|
||||
end
|
||||
|
||||
@ -1,21 +1,26 @@
|
||||
local form = require("form")
|
||||
local m, s, o
|
||||
|
||||
m = Map("wireless", translate("Guest WiFi"),
|
||||
translate("Guest WiFi provides a separate wireless network for guest access with isolated permissions."))
|
||||
translate("Guest WiFi provides a separate wireless network for guest access with isolated permissions."))
|
||||
|
||||
s = m:section(TypedSection, "wifi-iface", translate("Guest WiFi Settings"))
|
||||
s = m:section(form.TypedSection, "wifi-iface", translate("Guest WiFi Settings"))
|
||||
s.anonymous = true
|
||||
s.addremove = true
|
||||
s.filter = function(section_id)
|
||||
local val = uci:get("wireless", section_id, "guest_wifi")
|
||||
return val == "1"
|
||||
end
|
||||
|
||||
o = s:option(Flag, "guest_wifi", translate("Enable"),
|
||||
translate("Enable guest WiFi network"))
|
||||
o = s:option(form.Flag, "guest_wifi", translate("Enable"),
|
||||
translate("Enable guest WiFi network"))
|
||||
o.rmempty = false
|
||||
o.default = "0"
|
||||
|
||||
o = s:option(Value, "ssid", translate("Network Name (SSID)"))
|
||||
o = s:option(form.Value, "ssid", translate("Network Name (SSID)"))
|
||||
o.rmempty = false
|
||||
|
||||
o = s:option(ListValue, "encryption", translate("Encryption"))
|
||||
o = s:option(form.ListValue, "encryption", translate("Encryption"))
|
||||
o:value("none", translate("No Encryption"))
|
||||
o:value("psk2", translate("WPA2-PSK"))
|
||||
o:value("sae", translate("WPA3-SAE"))
|
||||
@ -23,7 +28,7 @@ o:value("sae-mixed", translate("WPA2/WPA3-Mixed"))
|
||||
o.rmempty = false
|
||||
o.default = "psk2"
|
||||
|
||||
o = s:option(Value, "key", translate("Password"))
|
||||
o = s:option(form.Value, "key", translate("Password"))
|
||||
o:depends("encryption", "psk2")
|
||||
o:depends("encryption", "sae")
|
||||
o:depends("encryption", "sae-mixed")
|
||||
@ -31,9 +36,9 @@ o.datatype = "wpakey"
|
||||
o.rmempty = false
|
||||
o.password = true
|
||||
|
||||
o = s:option(Flag, "isolate", translate("AP Isolation"),
|
||||
translate("Prevents wireless clients from communicating with each other"))
|
||||
o = s:option(form.Flag, "isolate", translate("AP Isolation"),
|
||||
translate("Prevents wireless clients from communicating with each other"))
|
||||
o.rmempty = false
|
||||
o.default = "1"
|
||||
|
||||
return m
|
||||
return m
|
||||
|
||||
@ -1,5 +1,14 @@
|
||||
msgid ""
|
||||
msgstr "Content-Type: text/plain; charset=UTF-8"
|
||||
msgstr ""
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Project-Id-Version: luci-app-guest-wifi\n"
|
||||
"PO-Revision-Date: 2024\n"
|
||||
"Last-Translator: kenzok8\n"
|
||||
"Language-Team: Chinese\n"
|
||||
"Language: zh_Hans\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=1; plural=0;\n"
|
||||
|
||||
msgid "Guest WiFi"
|
||||
msgstr ""
|
||||
@ -41,4 +50,4 @@ msgid "AP Isolation"
|
||||
msgstr ""
|
||||
|
||||
msgid "Prevents wireless clients from communicating with each other"
|
||||
msgstr ""
|
||||
msgstr ""
|
||||
|
||||
1
luci-app-guest-wifi/po/zh-cn
Symbolic link
1
luci-app-guest-wifi/po/zh-cn
Symbolic link
@ -0,0 +1 @@
|
||||
zh_Hans
|
||||
@ -1 +0,0 @@
|
||||
zh-cn
|
||||
@ -1,3 +1,15 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Project-Id-Version: luci-app-guest-wifi\n"
|
||||
"PO-Revision-Date: 2024\n"
|
||||
"Last-Translator: kenzok8\n"
|
||||
"Language-Team: Chinese\n"
|
||||
"Language: zh_Hans\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=1; plural=0;\n"
|
||||
|
||||
msgid "Guest WiFi"
|
||||
msgstr "访客WiFi"
|
||||
|
||||
@ -38,4 +50,4 @@ msgid "AP Isolation"
|
||||
msgstr "AP隔离"
|
||||
|
||||
msgid "Prevents wireless clients from communicating with each other"
|
||||
msgstr "防止无线客户端之间相互通信"
|
||||
msgstr "防止无线客户端之间相互通信"
|
||||
@ -1,3 +0,0 @@
|
||||
config guest-wifi 'config'
|
||||
option enabled '0'
|
||||
option isolate '1'
|
||||
@ -4,13 +4,13 @@ START=99
|
||||
USE_PROCD=1
|
||||
|
||||
reload_service() {
|
||||
wifi reload
|
||||
wifi reload >/dev/null 2>&1
|
||||
}
|
||||
|
||||
service_triggers() {
|
||||
procd_add_reload_trigger "wireless"
|
||||
procd_add_reload_trigger "wireless"
|
||||
}
|
||||
|
||||
start_service() {
|
||||
reload_service
|
||||
}
|
||||
reload_service
|
||||
}
|
||||
|
||||
19
luci-app-guest-wifi/root/etc/uci-defaults/40_luci-guest-wifi
Normal file → Executable file
19
luci-app-guest-wifi/root/etc/uci-defaults/40_luci-guest-wifi
Normal file → Executable file
@ -1,19 +1,6 @@
|
||||
#!/bin/sh
|
||||
[ ! -f "/usr/share/ucitrack/luci-app-guest-wifi.json" ] && {
|
||||
cat > /usr/share/ucitrack/luci-app-guest-wifi.json << EEOF
|
||||
{
|
||||
"config": "guest-wifi",
|
||||
"init": "guest-wifi"
|
||||
}
|
||||
EEOF
|
||||
}
|
||||
|
||||
uci -q batch <<-EOF >/dev/null
|
||||
delete ucitrack.@guest-wifi[-1]
|
||||
add ucitrack guest-wifi
|
||||
set ucitrack.@guest-wifi[-1].init=guest-wifi
|
||||
commit ucitrack
|
||||
EOF
|
||||
[ -z "${IPKG_INSTROOT}" ] || exit 0
|
||||
|
||||
rm -f /tmp/luci-indexcache
|
||||
exit 0
|
||||
rm -f /tmp/luci-indexcache 2>/dev/null
|
||||
exit 0
|
||||
|
||||
@ -1,8 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
uci -q batch <<-EOF >/dev/null
|
||||
set luci.languages.zh_cn='chinese'
|
||||
commit luci
|
||||
EOF
|
||||
|
||||
exit 0
|
||||
@ -1,9 +0,0 @@
|
||||
module("luci.controller.guest-wifi", package.seeall)
|
||||
|
||||
function index()
|
||||
if not nixio.fs.access("/etc/config/wireless") then
|
||||
return
|
||||
end
|
||||
|
||||
entry({"admin", "network", "guest-wifi"}, view("guest-wifi/wifi"), _("Guest WiFi"), 60)
|
||||
end
|
||||
@ -1,19 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
# 检查无线配置
|
||||
check_wireless() {
|
||||
local enabled
|
||||
config_load wireless
|
||||
config_get enabled guest_wifi enabled 0
|
||||
|
||||
if [ "$enabled" = "1" ]; then
|
||||
# 应用guest wifi配置
|
||||
wifi reload
|
||||
fi
|
||||
}
|
||||
|
||||
# 主循环
|
||||
while true; do
|
||||
check_wireless
|
||||
sleep 300
|
||||
done
|
||||
@ -1,14 +1,14 @@
|
||||
{
|
||||
"luci-app-guest-wifi": {
|
||||
"description": "Grant access to guest-wifi configuration",
|
||||
"read": {
|
||||
"uci": [ "wireless", "guest-wifi" ],
|
||||
"ubus": {
|
||||
"iwinfo": [ "devices", "info", "assoclist" ]
|
||||
}
|
||||
},
|
||||
"write": {
|
||||
"uci": [ "wireless", "guest-wifi" ]
|
||||
}
|
||||
}
|
||||
}
|
||||
"luci-app-guest-wifi": {
|
||||
"description": "Grant access to guest-wifi configuration",
|
||||
"read": {
|
||||
"uci": [ "wireless" ],
|
||||
"ubus": {
|
||||
"iwinfo": [ "devices", "info", "assoclist" ]
|
||||
}
|
||||
},
|
||||
"write": {
|
||||
"uci": [ "wireless" ]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -65,6 +65,9 @@ if singbox_tags:find("with_quic") then
|
||||
end
|
||||
o:value("anytls", "AnyTLS")
|
||||
o:value("ssh", "SSH")
|
||||
if singbox_tags:find("with_naive_outbound") then
|
||||
o:value("naive", "NaïveProxy")
|
||||
end
|
||||
o:value("_urltest", translate("URLTest"))
|
||||
o:value("_shunt", translate("Shunt"))
|
||||
o:value("_iface", translate("Custom Interface"))
|
||||
@ -224,6 +227,7 @@ o = s:option(Value, _n("username"), translate("Username"))
|
||||
o:depends({ [_n("protocol")] = "http" })
|
||||
o:depends({ [_n("protocol")] = "socks" })
|
||||
o:depends({ [_n("protocol")] = "ssh" })
|
||||
o:depends({ [_n("protocol")] = "naive" })
|
||||
|
||||
o = s:option(Value, _n("password"), translate("Password"))
|
||||
o.password = true
|
||||
@ -234,6 +238,7 @@ o:depends({ [_n("protocol")] = "trojan" })
|
||||
o:depends({ [_n("protocol")] = "tuic" })
|
||||
o:depends({ [_n("protocol")] = "anytls" })
|
||||
o:depends({ [_n("protocol")] = "ssh" })
|
||||
o:depends({ [_n("protocol")] = "naive" })
|
||||
|
||||
o = s:option(ListValue, _n("security"), translate("Encrypt Method"))
|
||||
for a, t in ipairs(security_list) do o:value(t) end
|
||||
@ -248,6 +253,7 @@ o:depends({ [_n("protocol")] = "shadowsocks" })
|
||||
o = s:option(Flag, _n("uot"), translate("UDP over TCP"))
|
||||
o:depends({ [_n("protocol")] = "socks" })
|
||||
o:depends({ [_n("protocol")] = "shadowsocks" })
|
||||
o:depends({ [_n("protocol")] = "naive" })
|
||||
|
||||
o = s:option(Value, _n("alter_id"), "Alter ID")
|
||||
o.datatype = "uinteger"
|
||||
@ -399,6 +405,26 @@ o = s:option(Value, _n("ssh_client_version"), translate("Client Version"), trans
|
||||
o:depends({ [_n("protocol")] = "ssh" })
|
||||
-- [[ SSH config end ]] --
|
||||
|
||||
-- [[ naive start ]] --
|
||||
o = s:option(Value, _n("naive_insecure_concurrency"), translate("Concurrent Tunnels"))
|
||||
o.datatype = "uinteger"
|
||||
o.placeholder = "0"
|
||||
o.default = "0"
|
||||
o:depends({ [_n("protocol")] = "naive" })
|
||||
|
||||
o = s:option(Flag, _n("naive_quic"), translate("QUIC"))
|
||||
o.default = 0
|
||||
o:depends({ [_n("protocol")] = "naive" })
|
||||
|
||||
o = s:option(ListValue, _n("naive_congestion_control"), translate("Congestion control algorithm"))
|
||||
o.default = "bbr"
|
||||
o:value("bbr", translate("BBR"))
|
||||
o:value("bbr2", translate("BBRv2"))
|
||||
o:value("cubic", translate("CUBIC"))
|
||||
o:value("reno", translate("New Reno"))
|
||||
o:depends({ [_n("naive_quic")] = "1" })
|
||||
-- [[ naive end ]] --
|
||||
|
||||
o = s:option(Flag, _n("tls"), translate("TLS"))
|
||||
o.default = 0
|
||||
o:depends({ [_n("protocol")] = "vmess" })
|
||||
@ -432,6 +458,7 @@ o:depends({ [_n("tls")] = true })
|
||||
o:depends({ [_n("protocol")] = "hysteria"})
|
||||
o:depends({ [_n("protocol")] = "tuic" })
|
||||
o:depends({ [_n("protocol")] = "hysteria2" })
|
||||
o:depends({ [_n("protocol")] = "naive" })
|
||||
|
||||
o = s:option(Flag, _n("tls_allowInsecure"), translate("allowInsecure"), translate("Whether unsafe connections are allowed. When checked, Certificate validation will be skipped."))
|
||||
o.default = "0"
|
||||
@ -446,6 +473,7 @@ o:depends({ [_n("tls")] = true, [_n("flow")] = "", [_n("reality")] = false })
|
||||
o:depends({ [_n("protocol")] = "tuic" })
|
||||
o:depends({ [_n("protocol")] = "hysteria" })
|
||||
o:depends({ [_n("protocol")] = "hysteria2" })
|
||||
o:depends({ [_n("protocol")] = "naive" })
|
||||
|
||||
o = s:option(TextValue, _n("ech_config"), translate("ECH Config"))
|
||||
o.default = ""
|
||||
@ -622,6 +650,7 @@ o:depends({ [_n("tcp_guise")] = "http" })
|
||||
o:depends({ [_n("transport")] = "http" })
|
||||
o:depends({ [_n("transport")] = "ws" })
|
||||
o:depends({ [_n("transport")] = "httpupgrade" })
|
||||
o:depends({ [_n("protocol")] = "naive" })
|
||||
|
||||
-- [[ Mux ]]--
|
||||
o = s:option(Flag, _n("mux"), translate("Mux"))
|
||||
|
||||
@ -154,7 +154,7 @@ function gen_outbound(flag, node, tag, proxy_table)
|
||||
}
|
||||
|
||||
local tls = nil
|
||||
if node.protocol == "hysteria" or node.protocol == "hysteria2" or node.protocol == "tuic" then
|
||||
if node.protocol == "hysteria" or node.protocol == "hysteria2" or node.protocol == "tuic" or node.protocol == "naive" then
|
||||
node.tls = "1"
|
||||
end
|
||||
if node.tls == "1" then
|
||||
@ -489,6 +489,23 @@ function gen_outbound(flag, node, tag, proxy_table)
|
||||
}
|
||||
end
|
||||
|
||||
if node.protocol == "naive" then
|
||||
protocol_table = {
|
||||
username = (node.username and node.username ~= "") and node.username or "",
|
||||
password = (node.password and node.password ~= "") and node.password or "",
|
||||
insecure_concurrency = tonumber(node.naive_insecure_concurrency or 0) > 0 and tonumber(node.naive_insecure_concurrency) or 0,
|
||||
udp_over_tcp = node.uot == "1" and {
|
||||
enabled = true,
|
||||
version = 2
|
||||
} or false,
|
||||
extra_headers = node.user_agent and {
|
||||
["User-Agent"] = node.user_agent
|
||||
} or nil,
|
||||
quic = node.naive_quic == "1" and true or false,
|
||||
quic_congestion_control = (node.naive_quic == "1" and node.naive_congestion_control) and node.naive_congestion_control or nil
|
||||
}
|
||||
end
|
||||
|
||||
if protocol_table then
|
||||
for key, value in pairs(protocol_table) do
|
||||
result[key] = value
|
||||
|
||||
@ -679,6 +679,31 @@ local current_node = map:get(section)
|
||||
params += opt.query("allowinsecure", dom_prefix + "tls_allowInsecure");
|
||||
}
|
||||
|
||||
params += "#" + encodeURI(v_alias.value);
|
||||
if (params[0] == "&") {
|
||||
params = params.substring(1);
|
||||
}
|
||||
url += params;
|
||||
} else if (v_type === "sing-box" && opt.get(dom_prefix + "protocol").value === "naive") {
|
||||
protocol = "naive+https";
|
||||
var v_username = opt.get(dom_prefix + "username");
|
||||
var v_password = opt.get(dom_prefix + "password");
|
||||
var v_port = opt.get(dom_prefix + "port");
|
||||
url = encodeURIComponent(v_username.value) +
|
||||
":" + encodeURIComponent(v_password.value) +
|
||||
"@" + _address +
|
||||
":" + v_port.value + "?";
|
||||
|
||||
var params = "security=tls";
|
||||
params += opt.query("sni", dom_prefix + "tls_serverName");
|
||||
params += opt.query("insecure-concurrency", dom_prefix + "naive_insecure_concurrency");
|
||||
params += opt.query("ech", dom_prefix + "ech_config");
|
||||
params += opt.query("uot", dom_prefix + "uot");
|
||||
if (opt.get(dom_prefix + "naive_quic")?.checked) {
|
||||
protocol = "naive+quic";
|
||||
params += opt.query("congestion_control", dom_prefix + "naive_congestion_control");
|
||||
}
|
||||
|
||||
params += "#" + encodeURI(v_alias.value);
|
||||
if (params[0] == "&") {
|
||||
params = params.substring(1);
|
||||
@ -1729,6 +1754,55 @@ local current_node = map:get(section)
|
||||
opt.set('remarks', decodeURIComponent(m.hash.substr(1)));
|
||||
}
|
||||
}
|
||||
if (ssu[0] === "naive+https" || ssu[0] === "naive+quic") {
|
||||
if (has_singbox) {
|
||||
dom_prefix = "singbox_"
|
||||
opt.set('type', "sing-box");
|
||||
}
|
||||
opt.set(dom_prefix + 'protocol', "naive");
|
||||
var _parsedUrl = new URL("http://" + ssu[1]);
|
||||
var username = _parsedUrl.username;
|
||||
var password = _parsedUrl.password;
|
||||
var hostname = _parsedUrl.hostname;
|
||||
var port = _parsedUrl.port;
|
||||
var search = _parsedUrl.search;
|
||||
var hash = _parsedUrl.hash;
|
||||
if (!username || !password) { //某些链接会把username和password之间的:进行编码
|
||||
const decoded = decodeURIComponent(username || password || "");
|
||||
const i = decoded.indexOf(":");
|
||||
if (i > -1) {
|
||||
username = decoded.slice(0, i);
|
||||
password = decoded.slice(i + 1);
|
||||
}
|
||||
}
|
||||
opt.set(dom_prefix + 'username', decodeURIComponent(username));
|
||||
opt.set(dom_prefix + 'password', decodeURIComponent(password));
|
||||
opt.set(dom_prefix + 'address', unbracketIP(hostname));
|
||||
opt.set(dom_prefix + 'port', port || "443");
|
||||
var queryParam = {};
|
||||
if (search.length > 1) {
|
||||
var query = search.split('?')
|
||||
var queryParams = query[1];
|
||||
var queryArray = queryParams.split('&');
|
||||
var params;
|
||||
for (i = 0; i < queryArray.length; i++) {
|
||||
params = queryArray[i].split('=');
|
||||
queryParam[decodeURIComponent(params[0])] = decodeURIComponent(params[1] || '');
|
||||
}
|
||||
}
|
||||
opt.set(dom_prefix + 'naive_insecure_concurrency', queryParam['insecure-concurrency'] || '0');
|
||||
opt.set(dom_prefix + 'uot', (queryParam.uot ?? '0') === '1');
|
||||
opt.set(dom_prefix + 'tls_serverName', queryParam.sni || '');
|
||||
opt.set(dom_prefix + 'ech', !!queryParam.ech);
|
||||
opt.set(dom_prefix + 'ech_config', queryParam.ech || '');
|
||||
if (ssu[0] === "naive+quic") {
|
||||
opt.set(dom_prefix + 'naive_quic', true);
|
||||
opt.set(dom_prefix + 'naive_congestion_control', queryParam.congestion_control || 'bbr');
|
||||
}
|
||||
if (hash) {
|
||||
opt.set('remarks', decodeURIComponent(hash.substr(1)));
|
||||
}
|
||||
}
|
||||
if (dom_prefix && dom_prefix != null) {
|
||||
if (opt.get(dom_prefix + 'port').value) {
|
||||
opt.get(dom_prefix + 'port').focus();
|
||||
|
||||
@ -2107,3 +2107,6 @@ msgstr "当前使用的 %s 节点"
|
||||
|
||||
msgid "Search nodes..."
|
||||
msgstr "搜索节点…"
|
||||
|
||||
msgid "Concurrent Tunnels"
|
||||
msgstr "并发隧道连接数"
|
||||
|
||||
@ -1561,6 +1561,68 @@ local function processData(szType, content, add_mode, group)
|
||||
return nil
|
||||
end
|
||||
end
|
||||
elseif szType == 'naive+https' or szType == 'naive+quic' then
|
||||
if has_singbox then
|
||||
result.type = 'sing-box'
|
||||
result.protocol = "naive"
|
||||
else
|
||||
log("跳过 NaïveProxy 节点,因未安装 NaïveProxy 核心程序 Sing-box。")
|
||||
return nil
|
||||
end
|
||||
|
||||
local alias = ""
|
||||
if content:find("#") then
|
||||
local idx_sp = content:find("#")
|
||||
alias = content:sub(idx_sp + 1, -1)
|
||||
content = content:sub(0, idx_sp - 1)
|
||||
end
|
||||
result.remarks = UrlDecode(alias)
|
||||
local Info = content
|
||||
if content:find("@", 1, true) then
|
||||
local contents = split(content, "@")
|
||||
local auth = contents[1] or ""
|
||||
local idx = auth:find(":", 1, true)
|
||||
if not idx then --修正某些链接会把username和password之间的:进行编码
|
||||
auth = UrlDecode(auth)
|
||||
idx = auth:find(":", 1, true)
|
||||
end
|
||||
if idx then
|
||||
result.username = UrlDecode(auth:sub(1, idx - 1))
|
||||
result.password = UrlDecode(auth:sub(idx + 1))
|
||||
end
|
||||
Info = (contents[2] or ""):gsub("/%?", "?")
|
||||
end
|
||||
local query = split(Info, "%?")
|
||||
local host_port = query[1]
|
||||
local params = {}
|
||||
for _, v in pairs(split(query[2], '&')) do
|
||||
local t = split(v, '=')
|
||||
if #t > 1 then
|
||||
params[string.lower(t[1])] = UrlDecode(t[2])
|
||||
end
|
||||
end
|
||||
if host_port:find(":") then
|
||||
local sp = split(host_port, ":")
|
||||
result.port = sp[#sp]
|
||||
if api.is_ipv6addrport(host_port) then
|
||||
result.address = api.get_ipv6_only(host_port)
|
||||
else
|
||||
result.address = sp[1]
|
||||
end
|
||||
else
|
||||
result.address = host_port
|
||||
end
|
||||
result.tls_serverName = params.sni
|
||||
result.uot = params.uot
|
||||
result.naive_insecure_concurrency = params["insecure-concurrency"] or "0"
|
||||
if params.ech and params.ech ~= "" then
|
||||
result.ech = "1"
|
||||
result.ech_config = params.ech
|
||||
end
|
||||
if szType == "naive+quic" then
|
||||
result.naive_quic = "1"
|
||||
result.naive_congestion_control = params.congestion_control or "bbr"
|
||||
end
|
||||
else
|
||||
log('暂时不支持' .. szType .. "类型的节点订阅,跳过此节点。")
|
||||
return nil
|
||||
|
||||
@ -10,8 +10,8 @@ THEME_NAME:=alpha
|
||||
THEME_TITLE:=Alpha
|
||||
|
||||
PKG_NAME:=luci-theme-$(THEME_NAME)
|
||||
PKG_VERSION:=3.9.6_beta
|
||||
PKG_RELEASE:=2
|
||||
PKG_VERSION:=3.9.7_beta
|
||||
PKG_RELEASE:=3
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
|
||||
|
||||
@ -30,29 +30,42 @@ return baseclass.extend({
|
||||
);
|
||||
document.querySelector(".main > .loading").style.opacity = "0";
|
||||
document.querySelector(".main > .loading").style.visibility = "hidden";
|
||||
if (window.innerWidth <= 1152)
|
||||
document.querySelector(".main-left").style.width = "0";
|
||||
if (window.innerWidth <= 1152) {
|
||||
document.querySelector(".main-left").style.transform = "translateX(-20rem)";
|
||||
}
|
||||
window.addEventListener("resize", this.handleSidebarToggle, true);
|
||||
},
|
||||
handleMenuExpand: function (ev) {
|
||||
var a = ev.target,
|
||||
ul1 = a.parentNode,
|
||||
ul2 = a.nextElementSibling;
|
||||
ul2 = a.nextElementSibling,
|
||||
isActive = ul1.classList.contains("active");
|
||||
document.querySelectorAll("li.slide.active").forEach(function (li) {
|
||||
if (li !== a.parentNode || li == ul1) {
|
||||
var menu = li.querySelector("ul");
|
||||
if (menu) {
|
||||
if (!menu.style.maxHeight || menu.style.maxHeight === "1200px") {
|
||||
menu.style.maxHeight = menu.scrollHeight + "px";
|
||||
}
|
||||
void menu.offsetHeight; // Force layout
|
||||
menu.style.maxHeight = "0px";
|
||||
}
|
||||
li.classList.remove("active");
|
||||
li.childNodes[0].classList.remove("active");
|
||||
}
|
||||
if (li == ul1) return;
|
||||
});
|
||||
if (!ul2) return;
|
||||
if (
|
||||
ul2.parentNode.offsetLeft + ul2.offsetWidth <=
|
||||
ul1.offsetLeft + ul1.offsetWidth
|
||||
)
|
||||
ul2.classList.add("align-left");
|
||||
ul1.classList.add("active");
|
||||
a.classList.add("active");
|
||||
if (!isActive) {
|
||||
if (
|
||||
ul2.parentNode.offsetLeft + ul2.offsetWidth <=
|
||||
ul1.offsetLeft + ul1.offsetWidth
|
||||
)
|
||||
ul2.classList.add("align-left");
|
||||
ul1.classList.add("active");
|
||||
a.classList.add("active");
|
||||
ul2.style.maxHeight = ul2.scrollHeight + "px";
|
||||
}
|
||||
a.blur();
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
@ -167,13 +180,38 @@ return baseclass.extend({
|
||||
darkMask = document.querySelector(".darkMask"),
|
||||
mainRight = document.querySelector(".main-right"),
|
||||
mainLeft = document.querySelector(".main-left"),
|
||||
open = mainLeft.style.width == "";
|
||||
if (width > 1152 || ev.type == "resize") open = true;
|
||||
darkMask.style.visibility = open ? "" : "visible";
|
||||
darkMask.style.opacity = open ? "" : 1;
|
||||
if (width <= 1152) mainLeft.style.width = open ? "0" : "";
|
||||
else mainLeft.style.width = "";
|
||||
mainLeft.style.visibility = open ? "" : "visible";
|
||||
mainRight.style["overflow-y"] = open ? "visible" : "hidden";
|
||||
open = mainLeft.style.transform === ""; // true if currently open
|
||||
|
||||
// If it's a resize event, we simulate that the sidebar was "open" so the logic below closes it for mobile, and opens it for desktop
|
||||
if (ev.type == "resize") {
|
||||
open = true;
|
||||
}
|
||||
|
||||
// Logics: 'open' true means we want to CLOSE it on mobile, but OPEN it on desktop.
|
||||
// Actually, let's make it clearer: we toggle the state.
|
||||
var willOpen = !open;
|
||||
if (ev.type == "resize") {
|
||||
willOpen = width > 1152;
|
||||
}
|
||||
|
||||
if (width <= 1152) {
|
||||
// Mobile behavior
|
||||
mainLeft.style.width = ""; // Reset width
|
||||
mainLeft.style.transform = willOpen ? "" : "translateX(-20rem)";
|
||||
mainLeft.style.visibility = willOpen ? "visible" : "";
|
||||
darkMask.style.visibility = willOpen ? "visible" : "";
|
||||
darkMask.style.opacity = willOpen ? 1 : "";
|
||||
mainRight.style.width = "";
|
||||
} else {
|
||||
// Desktop behavior
|
||||
mainLeft.style.width = ""; // Reset width
|
||||
mainLeft.style.transform = willOpen ? "" : "translateX(-20rem)";
|
||||
mainLeft.style.visibility = willOpen ? "visible" : "hidden";
|
||||
mainRight.style.width = willOpen ? "" : "100%";
|
||||
darkMask.style.visibility = "";
|
||||
darkMask.style.opacity = "";
|
||||
}
|
||||
if (ev && ev.preventDefault) ev.preventDefault();
|
||||
if (ev && ev.stopPropagation) ev.stopPropagation();
|
||||
},
|
||||
});
|
||||
|
||||
@ -783,6 +783,7 @@
|
||||
float: right;
|
||||
width: calc(100% - 16rem);
|
||||
height: 100%;
|
||||
transition: width 450ms cubic-bezier(0.16, 1, 0.3, 1);
|
||||
}
|
||||
|
||||
.nowrap:not(.td) {
|
||||
@ -795,6 +796,17 @@
|
||||
}
|
||||
|
||||
/* Sidebar */
|
||||
.showSide {
|
||||
position: -webkit-sticky;
|
||||
position: sticky;
|
||||
top: 0rem;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
margin-right: 0.7rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.main>.main-left {
|
||||
position: fixed;
|
||||
top: 4.8rem;
|
||||
@ -802,7 +814,7 @@
|
||||
float: left;
|
||||
overflow-x: auto;
|
||||
background-color: #2222359a;
|
||||
transition: visibility 400ms, width 400ms;
|
||||
transition: visibility 450ms, transform 450ms cubic-bezier(0.16, 1, 0.3, 1);
|
||||
border-top-left-radius: 0px;
|
||||
border-top-right-radius: 20px;
|
||||
border-bottom-left-radius: 0px;
|
||||
@ -2499,7 +2511,7 @@
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
content: "";
|
||||
transition: opacity 400ms, visibility 400ms;
|
||||
transition: opacity 450ms cubic-bezier(0.16, 1, 0.3, 1), visibility 450ms;
|
||||
visibility: hidden;
|
||||
opacity: 0;
|
||||
background-color: #2222359a;
|
||||
@ -2840,7 +2852,10 @@
|
||||
|
||||
.alert-message .btn,
|
||||
.modal .btn {
|
||||
padding: 0.3rem 0.6rem;
|
||||
padding: 0 1.2rem;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.container .alert,
|
||||
@ -2866,14 +2881,8 @@
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.main>.main-left>.nav>.slide>ul,
|
||||
.main>.main-left[style*="overflow: hidden"]>.nav>.slide>.menu::before,
|
||||
.tr.placeholder .td[data-title]::before,
|
||||
.cbi-dropdown>ul.preview,
|
||||
.cbi-dropdown>ul>li .hide-close,
|
||||
.cbi-dropdown[open]>ul.dropdown>li .hide-open,
|
||||
.hidden,
|
||||
.showSide,
|
||||
.node-main-login>.main>.main-left,
|
||||
[data-page^="admin-system-commands"] .panel-title,
|
||||
[data-page^="command-cfg"] .mobile-hide,
|
||||
@ -2881,7 +2890,6 @@
|
||||
display: none;
|
||||
}
|
||||
|
||||
.main>.main-left>.nav>.slide.active>ul,
|
||||
.cbi-dropdown[empty]>ul>li,
|
||||
.cbi-dropdown[optional][open]>ul.dropdown>li[placeholder],
|
||||
.cbi-dropdown[multiple][open]>ul.dropdown>li>form,
|
||||
@ -2892,6 +2900,21 @@
|
||||
display: block;
|
||||
}
|
||||
|
||||
.main>.main-left>.nav>.slide>ul {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
max-height: 0;
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
transition: max-height 0.4s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.3s ease-out, visibility 0.4s;
|
||||
}
|
||||
.main>.main-left>.nav>.slide.active>ul {
|
||||
max-height: 1200px; /* Fallback for initial load */
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
transition: max-height 0.4s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.4s ease-in, visibility 0s;
|
||||
}
|
||||
|
||||
.main>.main-left>.nav>li:hover,
|
||||
.main>.main-left>.nav>.slide>.menu:hover,
|
||||
.main>.main-left>.nav>.slide>.slide-menu>li:hover {
|
||||
@ -3194,16 +3217,26 @@
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.showSide {
|
||||
position: -webkit-sticky;
|
||||
position: sticky;
|
||||
top: 0rem;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
margin-right: 0.7rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
.showSide {
|
||||
position: -webkit-sticky !important;
|
||||
position: sticky !important;
|
||||
top: 0rem;
|
||||
display: inline-flex !important;
|
||||
align-items: center;
|
||||
min-width: 40px;
|
||||
height: 100%;
|
||||
margin-right: 0.7rem;
|
||||
cursor: pointer;
|
||||
flex-shrink: 0 !important;
|
||||
z-index: 3000 !important;
|
||||
}
|
||||
.showSide img {
|
||||
width: 30px !important;
|
||||
height: auto !important;
|
||||
display: block !important;
|
||||
visibility: visible !important;
|
||||
opacity: 1 !important;
|
||||
}
|
||||
|
||||
body:not(.logged-in) .showSide {
|
||||
visibility: hidden;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -1 +0,0 @@
|
||||
buat tambah login page disini
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 179 KiB After Width: | Height: | Size: 124 KiB |
@ -410,7 +410,8 @@ img.bawah {
|
||||
display: block;
|
||||
padding: 20px;
|
||||
text-decoration: none;
|
||||
width: 25rem;
|
||||
width: 14rem;
|
||||
max-width: 80vw;
|
||||
}
|
||||
|
||||
.icon-credits a {
|
||||
|
||||
@ -1,127 +1,108 @@
|
||||
<%#
|
||||
Alpha os is made from me for all .. especially for indo wrt members and fan of OpenWrt or DBAI Community
|
||||
luci-theme-alpha
|
||||
Copyright 2024 derisamedia <facebook.com/derisamedia>
|
||||
<%# Alpha os is made from me for all .. especially for indo wrt members and fan of OpenWrt or DBAI Community
|
||||
luci-theme-alpha Copyright 2024 derisamedia <facebook.com/derisamedia>
|
||||
Have a bug? Please create an issue here on GitHub!
|
||||
|
||||
luci-theme-material
|
||||
Copyright 2015 Lutty Yang <lutty@wcan.in>
|
||||
|
||||
luci-theme-bootstrap:
|
||||
Copyright 2008 Steven Barth <steven@midlink.org>
|
||||
Copyright 2008 Jo-Philipp Wich <jow@openwrt.org>
|
||||
Copyright 2012 David Menting <david@nut-bolt.nl>
|
||||
luci-theme-bootstrap:
|
||||
Copyright 2008 Steven Barth <steven@midlink.org>
|
||||
Copyright 2008 Jo-Philipp Wich <jow@openwrt.org>
|
||||
Copyright 2012 David Menting <david@nut-bolt.nl>
|
||||
|
||||
MUI:
|
||||
https://github.com/muicss/mui
|
||||
MUI:
|
||||
https://github.com/muicss/mui
|
||||
|
||||
Licensed to the public under the Apache License 2.0
|
||||
-%>
|
||||
<%
|
||||
local ver = require "luci.version"
|
||||
local uci = require "luci.model.uci".cursor()
|
||||
local config = uci:get_all("alpha", "theme")
|
||||
Licensed to the public under the Apache License 2.0
|
||||
-%>
|
||||
<% local ver=require "luci.version" local uci=require "luci.model.uci" .cursor() local
|
||||
config=uci:get_all("alpha", "theme" ) -- Table to store navbar icons local icon={} -- Iterate
|
||||
through all navbar entries uci:foreach("alpha", "navbar" , function(section) local
|
||||
address=section.address local icon_url=section.icon if address and icon_url then --
|
||||
Remove '/www/luci-static/alpha/' from icon_url
|
||||
icon_url=string.gsub(icon_url, "^/www/luci%-static/alpha/" , "" ) icon[address]=icon_url end
|
||||
end) -- Check navbar enable status local navbar_enabled=config.navbar=="1" -- Calculate number
|
||||
of enabled navbar links local num_links=0 uci:foreach("alpha", "navbar" , function(section) if
|
||||
section.enable=="Enable" then num_links=num_links + 1 end end) -- Calculate link width for
|
||||
responsive design local link_width=string.format("calc(100%% / %d)", num_links) -- Retrieve
|
||||
theme properties local background_color=config.color or '#2222359a' local
|
||||
blur_value=tonumber(config.blur) or 20 local link_blur=string.format("blur(%dpx)", blur_value)
|
||||
%>
|
||||
|
||||
-- Table to store navbar icons
|
||||
local icon = {}
|
||||
<style>
|
||||
.main>.main-left,
|
||||
.cbi-section,
|
||||
.cbi-section-error,
|
||||
#iptables,
|
||||
.Firewall form,
|
||||
#cbi-network>.cbi-section-node,
|
||||
#cbi-wireless>.cbi-section-node,
|
||||
#cbi-wireless>#wifi_assoclist_table,
|
||||
[data-tab-title],
|
||||
[data-page^="admin-system-admin"]:not(.node-main-login) .cbi-map:not(#cbi-dropbear),
|
||||
[data-page="admin-system-opkg"] #maincontent>.container,
|
||||
.tabs,
|
||||
.cbi-tabmenu,
|
||||
.cbi-tooltip {
|
||||
background-color: <%=background_color%>;
|
||||
backdrop-filter: <%=link_blur%>;
|
||||
-webkit-backdrop-filter: <%=link_blur%>;
|
||||
}
|
||||
|
||||
-- Iterate through all navbar entries
|
||||
uci:foreach("alpha", "navbar", function(section)
|
||||
local address = section.address
|
||||
local icon_url = section.icon
|
||||
@media screen and (max-width: 720px) {
|
||||
.navbar a {
|
||||
width: <%=link_width%>;
|
||||
}
|
||||
}
|
||||
|
||||
if address and icon_url then
|
||||
-- Remove '/www/luci-static/alpha/' from icon_url
|
||||
icon_url = string.gsub(icon_url, "^/www/luci%-static/alpha/", "")
|
||||
icon[address] = icon_url
|
||||
end
|
||||
end)
|
||||
<% if not navbar_enabled then %>.navbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
-- Check navbar enable status
|
||||
local navbar_enabled = config.navbar == "1"
|
||||
<% end %>
|
||||
</style>
|
||||
|
||||
-- Calculate number of enabled navbar links
|
||||
local num_links = 0
|
||||
uci:foreach("alpha", "navbar", function(section)
|
||||
if section.enable == "Enable" then
|
||||
num_links = num_links + 1
|
||||
end
|
||||
end)
|
||||
</div>
|
||||
<footer class="mobile">
|
||||
<a href="https://github.com/derisamedia/luci-theme-alpha">
|
||||
<%=ver.luciname%> | Alpha OS Theme v3.9.7
|
||||
</a>
|
||||
</footer>
|
||||
</div>
|
||||
<div class="navbar active">
|
||||
<% if navbar_enabled then %>
|
||||
<div class="dropdown">
|
||||
<% uci:foreach("alpha", "navbar" , function(section) local address=section.address
|
||||
local icon_url=icon[address] or 'default/icon.png' local
|
||||
target_blank=section.newtab=="Yes" and 'target="_blank"' or '' if
|
||||
section.enable=="Enable" then %>
|
||||
<a href="<%=address%>" <%=target_blank%>>
|
||||
<img src="<%=media%>/<%=icon_url%>" />
|
||||
</a>
|
||||
<% end end) %>
|
||||
</div>
|
||||
<% end %>
|
||||
<label class="toggler">
|
||||
<img src="<%=media%>/gaya/icon/arrow.svg">
|
||||
</label>
|
||||
</div>
|
||||
<script>
|
||||
{
|
||||
const nav = document.querySelector(".navbar");
|
||||
let lastScrollY = window.scrollY;
|
||||
window.addEventListener("scroll", () => {
|
||||
if (lastScrollY < window.scrollY) {
|
||||
nav.classList.add("navbar--hidden");
|
||||
} else {
|
||||
nav.classList.remove("navbar--hidden");
|
||||
}
|
||||
lastScrollY = window.scrollY;
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</div>
|
||||
<script type="text/javascript">L.require('menu-alpha')</script>
|
||||
<script src="<%=media%>/app.js"></script>
|
||||
</body>
|
||||
|
||||
-- Calculate link width for responsive design
|
||||
local link_width = string.format("calc(100%% / %d)", num_links)
|
||||
|
||||
-- Retrieve theme properties
|
||||
local background_color = config.color or '#2222359a'
|
||||
local blur_value = tonumber(config.blur) or 20
|
||||
local link_blur = string.format("blur(%dpx)", blur_value)
|
||||
%>
|
||||
|
||||
<style>
|
||||
.main > .main-left, .cbi-section, .cbi-section-error, #iptables, .Firewall form, #cbi-network > .cbi-section-node, #cbi-wireless > .cbi-section-node, #cbi-wireless > #wifi_assoclist_table, [data-tab-title], [data-page^="admin-system-admin"]:not(.node-main-login) .cbi-map:not(#cbi-dropbear), [data-page="admin-system-opkg"] #maincontent > .container, .tabs, .cbi-tabmenu, .cbi-tooltip {
|
||||
background-color: <%=background_color%>;
|
||||
backdrop-filter: <%=link_blur%>;
|
||||
-webkit-backdrop-filter: <%=link_blur%>;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 720px) {
|
||||
.navbar a {
|
||||
width: <%=link_width%>;
|
||||
}
|
||||
}
|
||||
|
||||
<% if not navbar_enabled then %>
|
||||
.navbar {
|
||||
display: none;
|
||||
}
|
||||
<% end %>
|
||||
</style>
|
||||
|
||||
</div>
|
||||
<footer class="mobile">
|
||||
<a href="https://github.com/derisamedia/luci-theme-alpha"><%=ver.luciname%> | Alpha OS Theme v3.9.5</a>
|
||||
</footer>
|
||||
</div>
|
||||
<div class="navbar active">
|
||||
<% if navbar_enabled then %>
|
||||
<div class="dropdown">
|
||||
<%
|
||||
uci:foreach("alpha", "navbar", function(section)
|
||||
local address = section.address
|
||||
local icon_url = icon[address] or 'default/icon.png'
|
||||
local target_blank = section.newtab == "Yes" and 'target="_blank"' or ''
|
||||
|
||||
if section.enable == "Enable" then
|
||||
%>
|
||||
<a href="<%=address%>" <%=target_blank%>>
|
||||
<img src="<%=media%>/<%=icon_url%>" />
|
||||
</a>
|
||||
<%
|
||||
end
|
||||
end)
|
||||
%>
|
||||
</div>
|
||||
<% end %>
|
||||
<label class="toggler">
|
||||
<img src="<%=media%>/gaya/icon/arrow.svg">
|
||||
</label>
|
||||
</div>
|
||||
<script>
|
||||
{
|
||||
const nav = document.querySelector(".navbar");
|
||||
let lastScrollY = window.scrollY;
|
||||
window.addEventListener("scroll", () => {
|
||||
if (lastScrollY < window.scrollY) {
|
||||
nav.classList.add("navbar--hidden");
|
||||
} else {
|
||||
nav.classList.remove("navbar--hidden");
|
||||
}
|
||||
lastScrollY = window.scrollY;
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</div>
|
||||
<script type="text/javascript">L.require('menu-alpha')</script>
|
||||
<script src="<%=media%>/app.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
@ -54,7 +54,7 @@
|
||||
|
||||
</div>
|
||||
<footer class="mobile">
|
||||
<a href="https://github.com/derisamedia/luci-theme-alpha">{{ version.luciname }} | Alpha OS Theme v3.9.5</a>
|
||||
<a href="https://github.com/derisamedia/luci-theme-alpha">{{ version.luciname }} | Alpha OS Theme v3.9.7</a>
|
||||
</footer>
|
||||
</div>
|
||||
<div class="navbar active">
|
||||
|
||||
@ -1,96 +1,108 @@
|
||||
<%#
|
||||
Alpha os is made from me for all .. especially for indo wrt members and fan of OpenWrt or DBAI Community
|
||||
luci-theme-alpha
|
||||
Copyright 2022 derisamedia <facebook.com/derisamedia>
|
||||
<%# Alpha os is made from me for all .. especially for indo wrt members and fan of OpenWrt or DBAI Community
|
||||
luci-theme-alpha Copyright 2022 derisamedia <facebook.com/derisamedia>
|
||||
Have a bug? Please create an issue here on GitHub!
|
||||
|
||||
|
||||
luci-theme-material
|
||||
Copyright 2015-2017 Lutty Yang <lutty@wcan.in>
|
||||
|
||||
luci-theme-bootstrap:
|
||||
Copyright 2008 Steven Barth <steven@midlink.org>
|
||||
Copyright 2008-2016 Jo-Philipp Wich <jow@openwrt.org>
|
||||
Copyright 2012 David Menting <david@nut-bolt.nl>
|
||||
luci-theme-bootstrap:
|
||||
Copyright 2008 Steven Barth <steven@midlink.org>
|
||||
Copyright 2008-2016 Jo-Philipp Wich <jow@openwrt.org>
|
||||
Copyright 2012 David Menting <david@nut-bolt.nl>
|
||||
|
||||
MUI:
|
||||
https://github.com/muicss/mui
|
||||
MUI:
|
||||
https://github.com/muicss/mui
|
||||
|
||||
Licensed to the public under the Apache License 2.0
|
||||
-%>
|
||||
Licensed to the public under the Apache License 2.0
|
||||
-%>
|
||||
|
||||
<%
|
||||
local sys = require "luci.sys"
|
||||
local util = require "luci.util"
|
||||
local http = require "luci.http"
|
||||
local disp = require "luci.dispatcher"
|
||||
local ver = require "luci.version"
|
||||
<% local sys=require "luci.sys" local util=require "luci.util" local http=require "luci.http" local
|
||||
disp=require "luci.dispatcher" local ver=require "luci.version" local
|
||||
boardinfo=util.ubus("system", "board" ) or { } local node=disp.context.dispatched local
|
||||
path=table.concat(disp.context.path, "-" ) http.prepare_content("text/html; charset=UTF-8") -%>
|
||||
<!DOCTYPE html>
|
||||
<html lang="<%=luci.i18n.context.lang%>">
|
||||
|
||||
local boardinfo = util.ubus("system", "board") or { }
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="color-scheme" content="dark">
|
||||
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0"
|
||||
name="viewport" />
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="mobile-web-app-capable" content="yes">
|
||||
<meta name="theme-color" content="#09c">
|
||||
<meta name="msapplication-tap-highlight" content="no">
|
||||
<meta name="msapplication-TileColor" content="#09c">
|
||||
<meta name="application-name" content="<%=striptags( (boardinfo.hostname or " ?") ) %> -
|
||||
LuCI">
|
||||
<meta name="apple-mobile-web-app-title" content="<%=striptags( (boardinfo.hostname or " ?")
|
||||
) %> - LuCI">
|
||||
<link rel="stylesheet" href="<%=media%>/gaya/gaya.css">
|
||||
<link rel="shortcut icon" href="<%=media%>/favicon.ico">
|
||||
<% if node and node.css then %>
|
||||
<link rel="stylesheet" href="<%=resource%>/<%=node.css%>">
|
||||
<% end -%>
|
||||
<script
|
||||
src="<%=url('admin/translations', luci.i18n.context.lang)%>?v=git-21.295.66888-fc702bc"></script>
|
||||
<script src="<%=resource%>/cbi.js?v=git-21.295.66888-fc702bc"></script>
|
||||
<title>
|
||||
<%=striptags( (boardinfo.hostname or "?" ) .. ( (node and node.title) and ' - '
|
||||
.. translate(node.title) or '' )) %> - LuCI
|
||||
</title>
|
||||
<% if css then %>
|
||||
<style title="text/css">
|
||||
<%=css %>
|
||||
</style>
|
||||
<% end -%>
|
||||
</head>
|
||||
|
||||
local node = disp.context.dispatched
|
||||
local path = table.concat(disp.context.path, "-")
|
||||
<body
|
||||
class="lang_<%=luci.i18n.context.lang%> <% if luci.dispatcher.context.authsession then %>logged-in<% end %> <% if not (path == "") then %>node-<%= path %><% else %>node-main-login<% end %>"
|
||||
data-page="<%= pcdata(path) %>"
|
||||
style="background-image:url('/luci-static/alpha/background/dashboard.png')">
|
||||
<header>
|
||||
<div class="fill">
|
||||
<div class="container">
|
||||
<a id="logo"
|
||||
href="<% if luci.dispatcher.context.authsession then %><%=url('admin/status/overview')%><% else %>#<% end %>">
|
||||
<img src="<%=media%>/brand.png" alt="OpenWrt">
|
||||
</a>
|
||||
<div class="status" id="indicators"></div>
|
||||
<div class="logout">
|
||||
<a href="/cgi-bin/luci/admin/logout" data-title="Logout">Keluar</a>
|
||||
</div>
|
||||
<div class="showSide">
|
||||
<img src="<%=media%>/gaya/icon/menu_icon.svg" alt="">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<div class="main">
|
||||
<div style="" class="loading"><span>
|
||||
<div class="loading-img"></div>
|
||||
</span></div>
|
||||
<div class="main-left" id="mainmenu" style="display:none"></div>
|
||||
<div class="main-right">
|
||||
<div class="modemenu-buttons" style="display:none">
|
||||
<ul id="modemenu"></ul>
|
||||
</div>
|
||||
<div class="darkMask"></div>
|
||||
<div id="maincontent">
|
||||
<div class="container">
|
||||
<%- if luci.sys.process.info("uid")==0 and luci.sys.user.getuser("root") and
|
||||
not luci.sys.user.getpasswd("root") and path
|
||||
~="admin-system-admin-password" then -%>
|
||||
<%- end -%>
|
||||
|
||||
http.prepare_content("text/html; charset=UTF-8")
|
||||
-%>
|
||||
<!DOCTYPE html>
|
||||
<html lang="<%=luci.i18n.context.lang%>">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" name="viewport"/>
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="mobile-web-app-capable" content="yes">
|
||||
<meta name="theme-color" content="#09c">
|
||||
<meta name="msapplication-tap-highlight" content="no">
|
||||
<meta name="msapplication-TileColor" content="#09c">
|
||||
<meta name="application-name" content="<%=striptags( (boardinfo.hostname or "?") ) %> - LuCI">
|
||||
<meta name="apple-mobile-web-app-title" content="<%=striptags( (boardinfo.hostname or "?") ) %> - LuCI">
|
||||
<link rel="stylesheet" href="<%=media%>/gaya/gaya.css">
|
||||
<link rel="shortcut icon" href="<%=media%>/favicon.ico">
|
||||
<% if node and node.css then %>
|
||||
<link rel="stylesheet" href="<%=resource%>/<%=node.css%>">
|
||||
<% end -%>
|
||||
<script src="<%=url('admin/translations', luci.i18n.context.lang)%>?v=git-21.295.66888-fc702bc"></script>
|
||||
<script src="<%=resource%>/cbi.js?v=git-21.295.66888-fc702bc"></script>
|
||||
<title><%=striptags( (boardinfo.hostname or "?") .. ( (node and node.title) and ' - ' .. translate(node.title) or '')) %> - LuCI</title>
|
||||
<% if css then %><style title="text/css">
|
||||
<%= css %>
|
||||
</style>
|
||||
<% end -%>
|
||||
</head>
|
||||
<body class="lang_<%=luci.i18n.context.lang%> <% if luci.dispatcher.context.authsession then %>logged-in<% end %> <% if not (path == "") then %>node-<%= path %><% else %>node-main-login<% end %>" data-page="<%= pcdata(path) %>" style="background-image:url('/luci-static/alpha/background/dashboard.png')">
|
||||
<header>
|
||||
<div class="fill">
|
||||
<div class="container">
|
||||
<a id="logo" href="<% if luci.dispatcher.context.authsession then %><%=url('admin/status/overview')%><% else %>#<% end %>">
|
||||
<img src="<%=media%>/brand.png" alt="OpenWrt">
|
||||
</a>
|
||||
<div class="status" id="indicators"></div>
|
||||
<div class="logout" >
|
||||
<a href="/cgi-bin/luci/admin/logout" data-title="Logout">Keluar</a>
|
||||
</div>
|
||||
<div class="showSide">
|
||||
<img src="<%=media%>/gaya/icon/menu_icon.svg" alt="">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<div class="main">
|
||||
<div style="" class="loading"><span><div class="loading-img"></div></span></div>
|
||||
<div class="main-left" id="mainmenu" style="display:none"></div>
|
||||
<div class="main-right">
|
||||
<div class="modemenu-buttons" style="display:none">
|
||||
<ul id="modemenu"></ul>
|
||||
</div>
|
||||
<div class="darkMask"></div>
|
||||
<div id="maincontent">
|
||||
<div class="container">
|
||||
<%- if luci.sys.process.info("uid") == 0 and luci.sys.user.getuser("root") and not luci.sys.user.getpasswd("root") and path ~= "admin-system-admin-password" then -%>
|
||||
<%- end -%>
|
||||
|
||||
<noscript>
|
||||
<div class="alert-message warning">
|
||||
<h4><%:JavaScript required!%></h4>
|
||||
<p><%:You must enable JavaScript in your browser or LuCI will not work properly.%></p>
|
||||
</div>
|
||||
</noscript>
|
||||
<div id="tabmenu" style="display:none"></div>
|
||||
<noscript>
|
||||
<div class="alert-message warning">
|
||||
<h4>
|
||||
<%:JavaScript required!%>
|
||||
</h4>
|
||||
<p>
|
||||
<%:You must enable JavaScript in your browser or LuCI
|
||||
will not work properly.%>
|
||||
</p>
|
||||
</div>
|
||||
</noscript>
|
||||
<div id="tabmenu" style="display:none"></div>
|
||||
@ -14,6 +14,7 @@
|
||||
<html lang="{{ dispatcher.lang }}">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="color-scheme" content="dark">
|
||||
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" name="viewport"/>
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="mobile-web-app-capable" content="yes">
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
<!-- saved from url=(0014)about:internet -->
|
||||
<html lang="en">
|
||||
<head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
|
||||
<meta name="color-scheme" content="dark">
|
||||
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" name="viewport">
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="mobile-web-app-capable" content="yes">
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
<html lang="{{ dispatcher.lang }}">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
|
||||
<meta name="color-scheme" content="dark">
|
||||
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" name="viewport">
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="mobile-web-app-capable" content="yes">
|
||||
|
||||
@ -1452,8 +1452,8 @@
|
||||
}
|
||||
|
||||
@layer table {
|
||||
.table,
|
||||
table {
|
||||
table.table,
|
||||
.table {
|
||||
@apply border-border max-md:bg-panel-bg/98 mb-3 w-full border-separate border-spacing-0 overflow-visible rounded-2xl border max-md:mx-0 max-md:block max-md:w-full max-md:overflow-visible max-md:border-0 max-md:shadow-none;
|
||||
|
||||
&[width="100%"] {
|
||||
|
||||
@ -8,8 +8,8 @@ include $(TOPDIR)/rules.mk
|
||||
LUCI_TITLE:=Aurora Theme (A modern browser theme built with Vite and Tailwind CSS)
|
||||
LUCI_DEPENDS:=+luci-base
|
||||
|
||||
PKG_VERSION:=0.11.1
|
||||
PKG_RELEASE:=2
|
||||
PKG_VERSION:=0.11.2
|
||||
PKG_RELEASE:=3
|
||||
PKG_LICENSE:=Apache-2.0
|
||||
|
||||
LUCI_MINIFY_CSS:=
|
||||
|
||||
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue
Block a user