mirror of
https://github.com/kiddin9/op-packages.git
synced 2026-09-10 18:34:18 +08:00
🎁 Sync 2026-08-05 20:25:34
This commit is contained in:
+2
-2
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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 |
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
|
||||
@@ -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:=
|
||||
|
||||
@@ -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`:
|
||||
|
||||
@@ -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`:
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user