🎁 Sync 2026-08-05 20:25:34

This commit is contained in:
github-actions[bot]
2026-08-05 20:25:34 +08:00
parent 67b57a52c7
commit 61841aa371
31 changed files with 374 additions and 113 deletions
+2 -2
View File
@@ -10,8 +10,8 @@ include $(TOPDIR)/rules.mk
PKG_ARCH_BAIDUDRIVE:=$(ARCH)
PKG_NAME:=baidudrive
PKG_VERSION:=baidudrive-runtime-v1.0.4
PKG_RELEASE:=5
PKG_VERSION:=linkease-runtime-v1.7.5
PKG_RELEASE:=6
PKG_SOURCE:=$(PKG_NAME)-binary-$(PKG_VERSION).tar.gz
PKG_SOURCE_URL:=https://github.com/istoreos/istoreos-app-hub/releases/download/baidudrive-runtime-v$(PKG_VERSION)/
PKG_HASH:=skip
+3 -3
View File
@@ -5,10 +5,10 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=dae
PKG_VERSION:=2026.08.03
PKG_RELEASE:=30
PKG_VERSION:=2026.08.05
PKG_RELEASE:=31
PKG_SOURCE:=dae-src-2026.08.03-354bb6787b4e.tar.gz
PKG_SOURCE:=dae-src-2026.08.05-03ce5c39b38c.tar.gz
PKG_SOURCE_URL:=https://github.com/kenzok8/openwrt-daede/releases/download/dae-src
PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION)
PKG_HASH:=skip
+3 -3
View File
@@ -5,10 +5,10 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=daed
PKG_VERSION:=2026.08.03
PKG_RELEASE:=41
PKG_VERSION:=2026.08.05
PKG_RELEASE:=42
PKG_SOURCE:=daed-src-2026.08.03-5f7939adc12a.tar.gz
PKG_SOURCE:=daed-src-2026.08.05-ebb45ff28107.tar.gz
PKG_SOURCE_URL:=https://github.com/kenzok8/openwrt-daede/releases/download/daed-src
PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION)
PKG_HASH:=skip
+2 -2
View File
@@ -5,8 +5,8 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=dnsproxy
PKG_VERSION:=0.83.1
PKG_RELEASE:=14
PKG_VERSION:=0.83.2
PKG_RELEASE:=15
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
PKG_SOURCE_URL:=https://codeload.github.com/AdguardTeam/dnsproxy/tar.gz/v$(PKG_VERSION)?
+2 -2
View File
@@ -6,8 +6,8 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=gowebdav
PKG_VERSION:=5.14.1
PKG_RELEASE:=18
PKG_VERSION:=5.14.2
PKG_RELEASE:=19
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
PKG_SOURCE_URL:=https://codeload.github.com/hacdias/webdav/tar.gz/v$(PKG_VERSION)?
Binary file not shown.

After

Width:  |  Height:  |  Size: 135 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

+1 -1
View File
@@ -12,7 +12,7 @@ LUCI_PKGARCH:=all
PKG_NAME:=luci-app-accesscontrol-plus
PKG_VERSION:=1
PKG_RELEASE:=1
PKG_RELEASE:=2
include $(TOPDIR)/feeds/luci/luci.mk
# call BuildPackage - OpenWrt buildroot signature
@@ -15,7 +15,7 @@ end
function act_status()
local e = {}
e.running = luci.sys.call("iptables -L INPUT |grep MIAPLUS >/dev/null") == 0
e.running = luci.sys.call("iptables -t filter -S MIAPLUS >/dev/null 2>&1") == 0
luci.http.prepare_content("application/json")
luci.http.write_json(e)
end
@@ -1,6 +1,10 @@
local ds = require "luci.dispatcher"
a = Map("miaplus")
a.apply_on_parse = true
function a.on_apply(self)
luci.sys.call("/etc/init.d/miaplus restart >/dev/null 2>&1 &")
end
t = a:section(TypedSection, "templates")
t.template = "cbi/tblsection"
@@ -3,6 +3,10 @@ local uci = luci.model.uci.cursor()
a = Map("miaplus")
a.title = translate("Internet Access Schedule Control Plus")
a.description = translate("Access Schedule Control Description")
a.apply_on_parse = true
function a.on_apply(self)
luci.sys.call("/etc/init.d/miaplus restart >/dev/null 2>&1 &")
end
a:section(SimpleSection).template = "miaplus/miaplus_status"
@@ -1,4 +1,8 @@
a = Map("miaplus")
a.apply_on_parse = true
function a.on_apply(self)
luci.sys.call("/etc/init.d/miaplus restart >/dev/null 2>&1 &")
end
local section = arg[1]
@@ -1,9 +1,15 @@
msgid "Internet Access Schedule Control Plus"
msgstr "上网时间控制Plus"
msgstr "上网时间控制增强版"
msgid "Access Schedule Control Description"
msgstr "设置客户端访问互联网的时间"
msgid "Base Setting"
msgstr "基础设置"
msgid "Advance Setting"
msgstr "高级设置"
msgid "General switch"
msgstr "开启/关闭"
@@ -19,6 +25,9 @@ msgstr "严格模式"
msgid "Strict Mode will degrade CPU performance, but it can achieve better results"
msgstr "严格模式会损耗部分CPU资源,但可以按照时间规则立即拦截数据包,效果更好"
msgid "IPV6 Enabled"
msgstr "启用 IPV6"
msgid "Client Rules"
msgstr "客户端规则"
@@ -10,6 +10,8 @@ START=30
CONFIG=miaplus
. /lib/functions.sh
uci_export_section_name() {
local ret=$(uci -n export $CONFIG | grep "config $1" | awk '{print $3}' | awk -F\' '{print $2}')
echo ${ret:=$2}
@@ -50,23 +52,18 @@ uci_get_mac_by_template(){
}
add_rules(){
local sections=$(uci_export_section_name templates '')
if [ -n "$sections" ]; then
for s in $sections
do
local enable=$(uci_get_by_name $s enable '' $i)
local title=$(uci_get_by_name $s title '' $i)
if [ -z $enable ]; then
break
fi
if [ "$enable" == "1" ]; then
add_rule $s
fi
done
fi
config_load "$CONFIG"
config_foreach add_template_rule templates
}
add_template_rule(){
local section="$1"
local enable
config_get enable "$section" enable 0
[ "$enable" = "1" ] && add_rule "$section"
}
add_rule(){
ipv6enable=$(uci get miaplus.@basic[0].ipv6enable)
ipv6enable=$(uci -q get miaplus.@basic[0].ipv6enable)
local macaddrs=$(uci_get_mac_by_template $1 '')
if [ -z "$macaddrs" ]; then
exit 0
@@ -74,7 +71,7 @@ add_rule(){
for macaddr in $macaddrs
do
iptables -t filter -A MIAPLUS -m mac --mac-source $macaddr -j DROP
[ $ipv6enable -eq 1 ] && ip6tables -t filter -A MIAPLUS -m mac --mac-source $macaddr -j DROP
[ "${ipv6enable:-0}" -eq 1 ] && ip6tables -t filter -A MIAPLUS -m mac --mac-source $macaddr -j DROP
done
for i in $(seq 0 100)
do
@@ -103,7 +100,7 @@ add_rule(){
for macaddr in $macaddrs
do
iptables -t filter -I MIAPLUS -m mac --mac-source $macaddr -m time --kerneltz --timestart $timeon --timestop $timeoff --weekdays $Z1$Z2$Z3$Z4$Z5$Z6$Z7 -j ACCEPT
[ $ipv6enable -eq 1 ] && ip6tables -t filter -I MIAPLUS -m mac --mac-source $macaddr -m time --kerneltz --timestart $timeon --timestop $timeoff --weekdays $Z1$Z2$Z3$Z4$Z5$Z6$Z7 -j ACCEPT
[ "${ipv6enable:-0}" -eq 1 ] && ip6tables -t filter -I MIAPLUS -m mac --mac-source $macaddr -m time --kerneltz --timestart $timeon --timestop $timeoff --weekdays $Z1$Z2$Z3$Z4$Z5$Z6$Z7 -j ACCEPT
done
fi
for n in $(seq 1 7)
@@ -126,27 +123,36 @@ del_rule(){
start(){
stop
enable=$(uci get miaplus.@basic[0].enable)
[ $enable -eq 0 ] && exit 0
enable=$(uci -q get miaplus.@basic[0].enable)
[ "${enable:-0}" -eq 0 ] && exit 0
iptables -t filter -N MIAPLUS
iptables -I INPUT -p udp --dport 53 -m comment --comment "Rule For Control" -j MIAPLUS
iptables -I INPUT -p tcp --dport 53 -m comment --comment "Rule For Control" -j MIAPLUS
iptables -t nat -A PREROUTING -p udp --dport 53 -j REDIRECT --to-ports 53 -m comment --comment "Rule For Control"
iptables -t nat -A PREROUTING -p tcp --dport 53 -j REDIRECT --to-ports 53 -m comment --comment "Rule For Control"
strict=$(uci get miaplus.@basic[0].strict)
[ $strict -eq 1 ] && iptables -t filter -I FORWARD -m comment --comment "Rule For Control" -j MIAPLUS
strict=$(uci -q get miaplus.@basic[0].strict)
[ "${strict:-0}" -eq 1 ] && iptables -t filter -I FORWARD -m comment --comment "Rule For Control" -j MIAPLUS
ipv6enable=$(uci get miaplus.@basic[0].ipv6enable)
if [ "$ipv6enable" -eq 1 ]; then
ipv6enable=$(uci -q get miaplus.@basic[0].ipv6enable)
if [ "${ipv6enable:-0}" -eq 1 ]; then
ip6tables -t filter -N MIAPLUS
ip6tables -I INPUT -p udp --dport 53 -m comment --comment "Rule For Control" -j MIAPLUS
ip6tables -I INPUT -p tcp --dport 53 -m comment --comment "Rule For Control" -j MIAPLUS
ip6tables -t nat -A PREROUTING -p udp --dport 53 -j REDIRECT --to-ports 53 -m comment --comment "Rule For Control"
ip6tables -t nat -A PREROUTING -p tcp --dport 53 -j REDIRECT --to-ports 53 -m comment --comment "Rule For Control"
[ $strict -eq 1 ] && ip6tables -t filter -I FORWARD -m comment --comment "Rule For Control" -j MIAPLUS
[ "${strict:-0}" -eq 1 ] && ip6tables -t filter -I FORWARD -m comment --comment "Rule For Control" -j MIAPLUS
fi
add_rules
}
status(){
if iptables -t filter -S MIAPLUS >/dev/null 2>&1; then
echo "running"
return 0
fi
echo "not running"
return 1
}
stop(){
iptables -t filter -D FORWARD -m comment --comment "Rule For Control" -j MIAPLUS 2>/dev/null
iptables -D INPUT -p udp --dport 53 -m comment --comment "Rule For Control" -j MIAPLUS 2>/dev/null
+1 -1
View File
@@ -15,7 +15,7 @@ PKG_MAINTAINER:=JiaY-shi <shi05275@163.com>
PKG_NAME:=luci-app-fancontrol
PKG_VERSION:=2.0.0
PKG_RELEASE:=2
PKG_RELEASE:=3
include $(TOPDIR)/feeds/luci/luci.mk
# call BuildPackage - OpenWrt buildroot signature
@@ -70,24 +70,38 @@
text-overflow: ellipsis;
}
.fancontrol-level {
height: 4px;
margin-top: .35rem;
overflow: hidden;
background: rgba(127, 127, 127, .2);
border-radius: 3px;
.fancontrol-chart-wrap {
height: 72px;
margin-top: .4rem;
}
.fancontrol-level > div {
.fancontrol-chart {
display: block;
width: 100%;
height: 100%;
background: var(--primary-color-medium, #1976d2);
border-radius: 3px;
transition: width .25s ease-in;
color: var(--text-color-medium, #666);
background: rgba(127, 127, 127, .025);
border: 1px solid rgba(127, 127, 127, .24);
}
.fancontrol-chart-temperature {
--fancontrol-chart-line: #c74b45;
--fancontrol-chart-fill: rgba(199, 75, 69, .2);
}
.fancontrol-chart-pwm {
--fancontrol-chart-line: #1976d2;
--fancontrol-chart-fill: rgba(25, 118, 210, .2);
}
.fancontrol-chart-rpm {
--fancontrol-chart-line: #2f8a57;
--fancontrol-chart-fill: rgba(47, 138, 87, .2);
}
.fancontrol-details {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: .3rem 1rem;
padding: .42rem .7rem;
background: rgba(127, 127, 127, .04);
@@ -102,10 +116,6 @@
min-width: 0;
}
.fancontrol-detail:last-child {
grid-column: 1 / -1;
}
.fancontrol-detail-value {
min-width: 0;
overflow-wrap: anywhere;
@@ -133,9 +143,6 @@
grid-template-columns: 1fr;
}
.fancontrol-detail:last-child {
grid-column: auto;
}
}
@media (max-width: 420px) {
@@ -14,6 +14,11 @@ const callServiceList = rpc.declare({
expect: { '': {} }
});
const HISTORY_WINDOW_MS = 2 * 60 * 1000;
const TIME_GRID_INTERVAL_MS = 10 * 1000;
const TIME_LABEL_INTERVAL_MS = 30 * 1000;
const VALUE_GRID_DIVISIONS = 4;
function serviceRunning(result) {
const instances = result?.fancontrol?.instances || {};
@@ -35,20 +40,173 @@ function parseStatus(text) {
return status;
}
function statusMetric(title, value, unit, level) {
function validStatusNumber(value) {
const number = Number(value);
return Number.isFinite(number) && number >= 0 ? number : null;
}
function appendHistory(history, running, status) {
const now = Date.now();
const temperature = validStatusNumber(status.temperature_mc);
const pwm = validStatusNumber(status.pwm);
const rpm = validStatusNumber(status.rpm);
history.push({
time: now,
temperature: running && temperature != null ? temperature / 1000 : null,
pwm: running && pwm != null ? pwm * 100 / 255 : null,
rpm: running ? rpm : null
});
while (history.length && history[0].time < now - HISTORY_WINDOW_MS)
history.shift();
}
function chartScale(history, key, minimumMax, step) {
let maximum = minimumMax;
history.forEach(function(sample) {
if (sample[key] != null)
maximum = Math.max(maximum, sample[key]);
});
return Math.ceil(maximum / step) * step;
}
function drawChart(canvas, history, key, options) {
const style = window.getComputedStyle(canvas);
const width = Math.max(canvas.clientWidth, 1);
const height = Math.max(canvas.clientHeight, 1);
const ratio = Math.min(window.devicePixelRatio || 1, 2);
const context = canvas.getContext('2d');
const padding = { left: 28, right: 5, top: 6, bottom: 14 };
const plotWidth = Math.max(width - padding.left - padding.right, 1);
const plotHeight = Math.max(height - padding.top - padding.bottom, 1);
const plotBottom = padding.top + plotHeight;
const now = history.length ? history[history.length - 1].time : Date.now();
const start = now - HISTORY_WINDOW_MS;
const maximum = chartScale(history, key, options.minimumMax, options.step);
const lineColor = style.getPropertyValue('--fancontrol-chart-line').trim() || '#1976d2';
const fillColor = style.getPropertyValue('--fancontrol-chart-fill').trim() || 'rgba(25, 118, 210, .2)';
const labelColor = style.color || '#666';
const gridColor = 'rgba(127, 127, 127, .2)';
let segment = [];
canvas.width = Math.round(width * ratio);
canvas.height = Math.round(height * ratio);
context.setTransform(ratio, 0, 0, ratio, 0, 0);
context.clearRect(0, 0, width, height);
context.strokeStyle = gridColor;
context.lineWidth = 1;
context.beginPath();
for (let elapsed = 0; elapsed <= HISTORY_WINDOW_MS; elapsed += TIME_GRID_INTERVAL_MS) {
const x = padding.left + plotWidth * elapsed / HISTORY_WINDOW_MS;
context.moveTo(x, padding.top);
context.lineTo(x, plotBottom);
}
for (let division = 0; division <= VALUE_GRID_DIVISIONS; division++) {
const y = padding.top + plotHeight * division / VALUE_GRID_DIVISIONS;
context.moveTo(padding.left, y);
context.lineTo(padding.left + plotWidth, y);
}
context.stroke();
context.fillStyle = labelColor;
context.font = '9px sans-serif';
context.textAlign = 'right';
context.textBaseline = 'top';
context.fillText(options.format(maximum), padding.left - 4, padding.top - 1);
context.textBaseline = 'bottom';
context.fillText(options.format(0), padding.left - 4, plotBottom + 1);
context.textBaseline = 'middle';
context.fillText(options.format(maximum / 2), padding.left - 4,
padding.top + plotHeight / 2);
context.textBaseline = 'bottom';
for (let elapsed = 0; elapsed <= HISTORY_WINDOW_MS; elapsed += TIME_LABEL_INTERVAL_MS) {
const x = padding.left + plotWidth * elapsed / HISTORY_WINDOW_MS;
const remaining = (HISTORY_WINDOW_MS - elapsed) / 1000;
context.textAlign = elapsed === 0 ? 'left' :
elapsed === HISTORY_WINDOW_MS ? 'right' : 'center';
context.fillText(remaining ? '-%ds'.format(remaining) : '0', x, height);
}
function point(sample) {
return {
x: padding.left + (sample.time - start) / HISTORY_WINDOW_MS * plotWidth,
y: plotBottom - sample[key] / maximum * plotHeight
};
}
function drawSegment() {
if (!segment.length)
return;
if (segment.length > 1) {
context.beginPath();
context.moveTo(segment[0].x, plotBottom);
segment.forEach(function(item) { context.lineTo(item.x, item.y); });
context.lineTo(segment[segment.length - 1].x, plotBottom);
context.closePath();
context.fillStyle = fillColor;
context.fill();
context.beginPath();
segment.forEach(function(item, index) {
if (index)
context.lineTo(item.x, item.y);
else
context.moveTo(item.x, item.y);
});
context.strokeStyle = lineColor;
context.lineWidth = 1.5;
context.lineJoin = 'round';
context.stroke();
} else {
context.beginPath();
context.arc(segment[0].x, segment[0].y, 2, 0, Math.PI * 2);
context.fillStyle = lineColor;
context.fill();
}
segment = [];
}
history.forEach(function(sample) {
if (sample[key] == null) {
drawSegment();
return;
}
segment.push(point(sample));
});
drawSegment();
}
function statusMetric(title, value, unit, chartClass) {
const canvas = E('canvas', {
'class': 'fancontrol-chart fancontrol-chart-%s'.format(chartClass),
'role': 'img',
'aria-label': title
});
const children = [
E('span', { 'class': 'fancontrol-metric-label' }, title),
E('div', { 'class': 'fancontrol-metric-reading' }, [
E('span', { 'class': 'fancontrol-metric-value' }, value),
unit ? E('span', { 'class': 'fancontrol-metric-unit' }, unit) : ''
])
]),
E('div', { 'class': 'fancontrol-chart-wrap' }, canvas)
];
if (level != null)
children.push(E('div', { 'class': 'fancontrol-level', 'aria-hidden': 'true' },
E('div', { 'style': 'width:%d%%'.format(level) })));
return E('div', { 'class': 'fancontrol-metric' }, children);
return {
node: E('div', { 'class': 'fancontrol-metric' }, children),
canvas: canvas
};
}
function statusDetail(title, value) {
@@ -58,7 +216,7 @@ function statusDetail(title, value) {
]);
}
function renderStatus(node, running, status) {
function renderStatus(node, running, status, history) {
const temp = Number(status.temperature_mc);
const pwm = Number(status.pwm);
const rpm = Number(status.rpm);
@@ -69,6 +227,12 @@ function renderStatus(node, running, status) {
const fault = status.error
? E('span', { 'class': 'label warning' }, status.error)
: E('span', { 'class': 'fancontrol-ok' }, _('None'));
const temperatureMetric = statusMetric(_('Temperature'),
tempValid ? '%.1f'.format(temp / 1000) : '-', tempValid ? '°C' : '', 'temperature');
const pwmMetric = statusMetric(_('PWM output'), pwmValid ? String(pwm) : '-',
pwmValid ? '/ 255 · %d%%'.format(pwmPercent) : '', 'pwm');
const rpmMetric = statusMetric(_('Fan speed'), rpmValid ? String(rpm) : '-',
rpmValid ? 'RPM' : '', 'rpm');
dom.content(node, E('div', { 'class': 'fancontrol-status' }, [
E('div', { 'class': 'fancontrol-status-head' }, [
@@ -77,11 +241,9 @@ function renderStatus(node, running, status) {
running ? _('Running') : _('Stopped'))
]),
E('div', { 'class': 'fancontrol-metrics' }, [
statusMetric(_('Temperature'), tempValid ? '%.1f'.format(temp / 1000) : '-',
tempValid ? '°C' : ''),
statusMetric(_('PWM output'), pwmValid ? String(pwm) : '-',
pwmValid ? '/ 255 · %d%%'.format(pwmPercent) : '', pwmPercent),
statusMetric(_('Fan speed'), rpmValid ? String(rpm) : '-', rpmValid ? 'RPM' : '')
temperatureMetric.node,
pwmMetric.node,
rpmMetric.node
]),
E('div', { 'class': 'fancontrol-details' }, [
statusDetail(_('Temperature input'), status.thermal_file || '-'),
@@ -89,6 +251,22 @@ function renderStatus(node, running, status) {
statusDetail(_('Fault'), fault)
])
]));
drawChart(temperatureMetric.canvas, history, 'temperature', {
minimumMax: 100,
step: 10,
format: function(value) { return '%d°'.format(value); }
});
drawChart(pwmMetric.canvas, history, 'pwm', {
minimumMax: 100,
step: 100,
format: function(value) { return '%d%%'.format(value); }
});
drawChart(rpmMetric.canvas, history, 'rpm', {
minimumMax: 1000,
step: 500,
format: function(value) { return String(value); }
});
}
function validateAutoPath(sectionId, value) {
@@ -114,6 +292,7 @@ return view.extend({
s.anonymous = true;
s.addremove = false;
s.render = function() {
const history = [];
const node = E('div', { 'class': 'cbi-section fancontrol-runtime' }, [
E('link', {
'rel': 'stylesheet',
@@ -128,7 +307,11 @@ return view.extend({
L.resolveDefault(callServiceList('fancontrol'), {}),
L.resolveDefault(fs.read_direct('/var/run/fancontrol.status'), '')
]).then(function(result) {
renderStatus(statusNode, serviceRunning(result[0]), parseStatus(result[1]));
const running = serviceRunning(result[0]);
const status = parseStatus(result[1]);
appendHistory(history, running, status);
renderStatus(statusNode, running, status, history);
});
}, 5);
@@ -890,14 +890,6 @@ return view.extend({
so.depends('type', 'zerotier');
so.modalonly = true;
so = ss.taboption('field_general', form.ListValue, 'zerotier_ipstack', _('IP stack'));
so.value('', _('Keep default'));
so.value('auto', _('Auto'));
so.value('gvisor', _('gVisor'));
so.value('mips', _('mihomo IP stack (MIPS)'));
so.depends('type', 'zerotier');
so.modalonly = true;
/* WireGuard fields */
so = ss.taboption('field_general', hm.GenValue, 'wireguard_private_key', _('Private key'),
_('WireGuard requires base64-encoded private keys.'));
@@ -1048,6 +1040,14 @@ return view.extend({
so.modalonly = true;
/* Extra fields */
so = ss.taboption('field_general', form.ListValue, 'ipstack', _('IP stack'));
so.value('', _('Keep default'));
so.value('auto', _('Auto'));
so.value('gvisor', _('gVisor'));
so.value('mips', _('mihomo IP stack (MIPS)'));
so.depends({type: /^(zerotier|wireguard|masque|openvpn)$/});
so.modalonly = true;
so = ss.taboption('field_general', form.ListValue, 'congestion_controller', _('Congestion controller'));
so.default = hm.congestion_controller[0][0];
hm.congestion_controller.forEach((res) => {
@@ -1055,7 +1055,7 @@ return view.extend({
})
so.depends({type: /^(tuic|shadowquic|trusttunnel)$/});
so.depends({type: 'masque', masque_network: /^(|h3-l4proxy)$/});
so.depends({type: 'zerotier', zerotier_ipstack: /^(auto|mips)$/}); // not empty not gvisor
so.depends({ipstack: /^(auto|mips)$/}); // not empty not gvisor
so.modalonly = true;
so = ss.taboption('field_general', form.ListValue, 'bbr_profile', _('BBR profile'));
@@ -163,6 +163,14 @@ migrate() {
config_list_foreach "$1" masque_dns add_list "$CONF" "$1" endpoint_dns
uci_remove "$CONF" "$1" masque_dns
fi
# zerotier_ipstack -> ipstack
if isDefined "$1" zerotier_ipstack; then
local zerotier_ipstack
config_get zerotier_ipstack "$1" zerotier_ipstack ""
uci_remove "$CONF" "$1" zerotier_ipstack
uci_set "$CONF" "$1" ipstack "$zerotier_ipstack"
fi
}
config_foreach migrate node
@@ -652,10 +652,6 @@ uci.foreach(uciconf, ucinode, (cfg) => {
return orbits;
})[0]
}),
"ip-stack": cfg.zerotier_ipstack ? {
mode: cfg.zerotier_ipstack,
"congestion-controller": replace(cfg.congestion_controller, 'new_reno', 'reno')
} : null,
/* WireGuard */
"pre-shared-key": cfg.wireguard_pre_shared_key,
@@ -680,10 +676,14 @@ uci.foreach(uciconf, ucinode, (cfg) => {
"host-key": cfg.ssh_host_key,
/* Extra fields */
"ip-stack": cfg.ipstack ? {
mode: cfg.ipstack,
"congestion-controller": replace(cfg.congestion_controller, 'new_reno', 'reno')
} : null,
"udp-over-stream": strToBool(cfg.shadowquic_udp_over_stream || cfg.tuic_udp_over_stream),
"heartbeat-interval": strToInt(cfg.tuic_heartbeat) || null,
"keep-alive-interval": strToInt(cfg.shadowquic_heartbeat) || null,
"congestion-controller": cfg.type in ['zerotier'] ? null : cfg.congestion_controller,
"congestion-controller": cfg.type in ['zerotier', 'wireguard', 'masque', 'openvpn'] ? null : cfg.congestion_controller,
"bbr-profile": cfg.bbr_profile,
"max-open-streams": strToInt(cfg.max_open_streams) || null,
+1 -1
View File
@@ -9,7 +9,7 @@ LUCI_TITLE:=Aurora Theme (A modern browser theme built with Vite and Tailwind CS
LUCI_DEPENDS:=+luci-base
PKG_VERSION:=1.1.13
PKG_RELEASE:=64
PKG_RELEASE:=65
PKG_LICENSE:=Apache-2.0
LUCI_MINIFY_CSS:=
+2
View File
@@ -64,6 +64,8 @@ wget -qO- https://openwrt.eamonxg.fun/install.sh | sh
That is the whole installation — the script adds the feed and installs whatever you tick from the list it shows. Upgrade later with the usual commands: `apk update && apk upgrade luci-theme-aurora`, or `opkg update && opkg upgrade luci-theme-aurora`. Details: [openwrt.eamonxg.fun](https://openwrt.eamonxg.fun/).
> **apk**: if this package was previously installed from a downloaded `.apk` file, that pinned it in `/etc/apk/world` and `apk upgrade` will silently do nothing — success reported, version unchanged. Run `apk add luci-theme-aurora` once (the name, no path) to clear the pin. The script above already does this for you.
### Via a GitHub release
OpenWrt 25.12+ and snapshots use `apk`; other versions use `opkg`:
+2
View File
@@ -64,6 +64,8 @@ wget -qO- https://openwrt.eamonxg.fun/install.sh | sh
这就是全部安装步骤——脚本会添加软件源,并安装您从列表中勾选的软件包。之后用常规命令升级即可:`apk update && apk upgrade luci-theme-aurora`,或 `opkg update && opkg upgrade luci-theme-aurora`。详细信息见 [openwrt.eamonxg.fun](https://openwrt.eamonxg.fun/)。
> **apk**:若此前是从下载的 `.apk` 文件安装的,该文件会把软件包钉死在 `/etc/apk/world` 里,此后 `apk upgrade` 会静默地什么都不做——报告成功,版本却没变。执行一次 `apk add luci-theme-aurora`(只写包名,不带路径)即可解除。上面的脚本已自动处理这一步。
### 通过 GitHub Release
OpenWrt 25.12+ 和 Snapshot 版本使用 `apk`;其他版本使用 `opkg`
+1 -1
View File
@@ -17,7 +17,7 @@ LUCI_NAME:=luci-theme-footstrap
FOOTSTRAP_VERSION?=
ifneq ($(FOOTSTRAP_VERSION),)
PKG_VERSION:=$(FOOTSTRAP_VERSION)
PKG_RELEASE:=5
PKG_RELEASE:=6
endif
LUCI_TITLE:=Footstrap Theme
@@ -519,10 +519,14 @@ function silence(el) {
* A foreign sheet is injected by ONE page and has no business painting any other. Before this, an
* invasive sheet made the whole document spent (documentPoisoned) and the SPA fell back to a full
* load on the way OUT correct, and paid by ORDINARY pages: `luci-app-filemanager`, a stock app,
* calls its `insertCss()` at MODULE EVAL and lands two <style>s in <head> (its own rules plus the
* HexEditor's), and `luci-app-ssclash` adds four more as the Ace editor initialises. Both carry
* real properties on bare selectors (`.hexview:focus`, `.ace_gutter`), which is invasive by the
* only definition that also catches `[class] { padding: 0 !important }`.
* lands TWO <style>s in <head>. Its HexEditor module calls `injectHexEditorCSS()` at MODULE EVAL
* and the view's own `render()` calls `insertCss()` on every arrival; both are invasive on their
* BARE selectors, not on the ones pinned to `#file-manager-container` HexEditor declares
* `:root { --span-spacing; --clr-background; … }`, the view adds `:root`, `.cbi-page-actions`,
* `.cbi-button-save:not(.custom-save-button)` and a `td:last-child` riding as the second half of
* `#file-manager-container th:last-child, td:last-child`. `luci-app-ssclash` adds four more as the
* Ace editor initialises. Invasive by the only definition that also catches
* `[class] { padding: 0 !important }`.
*
* Measured on owrt2512, 25.12.4, with ownership taken out of documentPoisoned() and put back:
* leaving either page is a FULL LOAD, 5 runs of 5, and with ownership all 5 are in place
@@ -531,9 +535,13 @@ function silence(el) {
* 24.10/25.12 the realtime graphs style their SVG text with an inline `style=` attribute so
* what this costs a router is decided entirely by which apps are installed on it.
*
* Removing the sheet on the way out is NOT the fix, and it is the obvious one: the append is at
* module top level, `L.require` caches the module, so a second visit re-runs nothing and the page
* would render unstyled. Disabling is reversible, which is the whole difference.
* Removing the sheet on the way out is NOT the fix, and it is the obvious one: an append at MODULE
* TOP LEVEL happens once, because `L.require` caches the module, so a second visit re-runs nothing
* and the page renders unstyled HexEditor's injector above is exactly that shape, and its own
* `getElementById('hexeditor-styles')` guard never gets a second chance to notice the element is
* gone, because nothing calls it again. A sheet injected from
* `render()` would survive removal, but the two cases are indistinguishable from here and only one
* mechanism can be right for both. Disabling is reversible, which is the whole difference.
*
* OWNER = body[data-page] when the sheet was re-hosted. The order that makes this sound is in
* fs-router.js: it stamps data-page (line ~371) BEFORE require()ing the view class (~450), so at the
@@ -101,8 +101,9 @@ mkdir -p /etc/footstrap 2>/dev/null
# response by extension, and an SVG served as application/octet-stream is one no browser will
# paint — as a CSS mask, as a background image or at all.
[ -d /www/luci-static/footstrap ] && ln -sf /etc/footstrap/pattern.svg /www/luci-static/footstrap/pattern.svg 2>/dev/null
# The admin-installed webfonts (fonts/set-font.sh) are a whole DIRECTORY rather than one file: the
# faces plus the @font-face sheet generated next to them. Same arrangement as the two images above,
# The admin-installed webfonts (/etc/footstrap/fonts, see partials/head.ut) are a whole DIRECTORY
# rather than one file: the faces plus an @font-face sheet beside them. Same arrangement as the two
# images above,
# and linking a directory has TWO traps, both of which end with `ln` exiting 0 and the link sitting
# one level too deep, at .../fonts/fonts — where no browser looks, so the fonts silently do not
# serve and nothing reports a thing:
@@ -1,7 +1,7 @@
# Preserve what the ADMIN put on this router across sysupgrade: the login/page background
# (Appearance -> Wallpaper -> File), the wallpaper pattern (Appearance -> Wallpaper -> Pattern) and
# the webfonts installed by fonts/set-font.sh. All three are written at runtime — an upload or a
# script, never the package — so all three survive a package upgrade on their own; a firmware
# the webfonts the admin put in /etc/footstrap/fonts. All three are written at runtime — an upload
# or the admin, never the package — so all three survive a package upgrade on their own; a firmware
# sysupgrade, however, only keeps paths listed here (or in a conffile / /etc/sysupgrade.conf).
# NOT conffiles: they live outside root/etc/config, and tools/conffiles.mjs would reject a
# conffiles-define entry the package does not ship.
@@ -64,8 +64,9 @@
/* the fonts, same shape and for the same reason: no browser layer, no Appearance control,
no entry in snapshotAxes(). font_sans/font_mono are font-family stacks (head.ut
whitelists the charset and prints them into a style element); `fonts` is the cache-bust
token of the generated @font-face sheet, hex like the two above. All three are written by
fonts/set-font.sh or by hand with `uci set`, never by Save as default. */
token of the @font-face sheet the admin installs beside the faces, hex like the two above.
All three are written with `uci set` — by hand, or by the one-line installer that lives in
the theme's own repository and not in this package — never by Save as default. */
'font_sans', 'font_mono', 'fonts'
];
const fs_defaults = {};
@@ -85,21 +85,42 @@
styles/02-tokens.css are what everyone gets unless the ROUTER names something else here.
Two independent things, either usable alone:
font_sans / font_mono a font-family stack, printed into a style element further down.
fonts the cache-bust token of the @font-face sheet fonts/set-font.sh
generates when an admin also installs the FILE. Hex, exactly like
login_bg and pattern, and for the same reason.
fonts the cache-bust token (an md5) of an @font-face sheet at
/etc/footstrap/fonts/fonts.css, for when the admin installs the
FILES too. Hex, exactly like login_bg and pattern, and for the same
reason.
THE PACKAGE IS THE SERVING HALF ONLY. It reads these three options, serves whatever is in
/etc/footstrap/fonts (uci-defaults links it under /www) and keeps that directory across a
sysupgrade — it installs no faces and fetches nothing at run time, which is the rule the
whole theme is built on. Putting the files there is the admin's move: drop the .woff2 files
and a fonts.css beside them, then `uci set footstrap.settings.fonts=<md5 of fonts.css>`.
`fonts/set-font.sh` in the theme's OWN repository does exactly that in one line and is NOT
part of this package — it names third-party font hosts, which is precisely what a theme in
this tree must not carry.
Neither is an axis: there is no browser layer, no Appearance control and no entry in
snapshotAxes() — a font is a router-wide decision here, not a per-visitor one.
THE CHARSET IS THE WHOLE DEFENCE. A family stack is free text and it is printed UNESCAPED,
THE CHARSET IS MOST OF THE DEFENCE. A family stack is free text and it is printed UNESCAPED,
so the whitelist has to exclude everything that could end the declaration or the element:
< > ( ) { } ; and the backslash are all absent, which leaves no way to close a style element,
start a second declaration or write a url(). Anything else becomes '' = "no router font".
A Cyrillic or CJK family name is refused by this too — those faces carry a Latin name as well,
and fonts/README.md says to use it. */
and the tool's README says to use it.
THE QUOTE IS THE ONE CHARACTER THE CHARSET CANNOT REFUSE, and it does not end anything — it
EXTENDS. Both quotes have to be in the class (the shipped default is `"Manrope",system-ui,…`),
and a value with an ODD count of either opens a CSS string that runs to the end of the sheet:
`font_sans=My"Font` swallows `; --fs-font-mono: …; }` with it, so a typo in the sans stack
silently costs the MONO one on a router that set that one correctly. Two options this file
calls independent, and one takes the other down. So count them and refuse the odd case: n
quotes split a string into n+1 parts, hence the even-length test. */
function _sd_family(v) {
return (type(v) == 'string' && length(v) >= 1 && length(v) <= 120 &&
match(v, /^[A-Za-z0-9 ,._"'-]+$/)) ? v : '';
if (!(type(v) == 'string' && length(v) >= 1 && length(v) <= 120 &&
match(v, /^[A-Za-z0-9 ,._"'-]+$/)))
return '';
if ((length(split(v, '"')) % 2) == 0 || (length(split(v, "'")) % 2) == 0)
return '';
return v;
}
let _sd_fsans = _sd_family(fs_defaults.font_sans);
let _sd_fmono = _sd_family(fs_defaults.font_mono);
@@ -354,13 +375,14 @@
no webfonts: --fs-font-sans and --fs-font-mono name Manrope and JetBrains Mono FIRST and
the system stack after, so a browser that has either installed locally uses it and one
that does not falls through silently — no request, no 404, no flash. What follows the
stylesheet below is the OPT-IN: a router whose admin ran fonts/set-font.sh.
stylesheet below is the OPT-IN: a router whose admin installed faces of their own under
/etc/footstrap/fonts (see the option block at the top of this file).
The preload pair is why both tags down there are written the way they are. A preload is
not in the stylesheet, so deleting the @font-face rules alone left these two behind and
every page went on asking the router for files that were gone: SIX 404s per page, and
nothing in CSS could have told anyone. So the sheet link is emitted only when the token
says the sheet exists, and the token is written by set-font.sh only after the file is. #}
nothing in CSS could have told anyone. So the sheet link is emitted only when the TOKEN
says the sheet exists — set the option after the file, never before. #}
{# `data-fs-shell` marks the SHELL's own CSS: on every nav the SPA router asks whether a
VIEW put CSS in the document, and this is how it tells its own sheet from an app's. #}
<link rel="stylesheet" data-fs-shell href="{{ media }}/cascade.css?v={{ pkgs_update_time }}">
+2 -2
View File
@@ -10,12 +10,12 @@ include $(INCLUDE_DIR)/kernel.mk
PKG_NAME:=natflow
PKG_VERSION:=20260531
PKG_RELEASE:=59
PKG_RELEASE:=60
PKG_SOURCE:=$(PKG_VERSION).tar.xz
PKG_SOURCE_URL:=https://github.com/ptpt52/natflow.git
PKG_SOURCE_PROTO:=git
PKG_SOURCE_VERSION:=9061fb3c2974689a4dfd118f607a400d47d6b16f
PKG_SOURCE_VERSION:=c4c0370953045bac23728fbeff456fc584cd9c80
PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION)
PKG_MAINTAINER:=Chen Minqiang <ptpt52@gmail.com>
PKG_LICENSE:=GPL-2.0