🗽 Sync 2026-08-04 20:40:02

This commit is contained in:
github-actions[bot] committed 2026-08-04 20:40:02 +08:00
1 parent d56fd78efc
commit 9b522ebf42
144 files changed
+4375 -3375

No files matched your search

+1 -1
View File
@@ -7,7 +7,7 @@
include $(TOPDIR)/rules.mk include $(TOPDIR)/rules.mk
PKG_NAME:=airconnect PKG_NAME:=airconnect
PKG_VERSION:=1.10.1 PKG_VERSION:=1.11.1
PKG_RELEASE=1 PKG_RELEASE=1
PKG_SOURCE:=AirConnect-$(PKG_VERSION).zip PKG_SOURCE:=AirConnect-$(PKG_VERSION).zip
+1 -1
View File
@@ -6,7 +6,7 @@ include $(TOPDIR)/rules.mk
PKG_NAME:=daed PKG_NAME:=daed
PKG_VERSION:=2026.08.03 PKG_VERSION:=2026.08.03
PKG_RELEASE:=40 PKG_RELEASE:=41
PKG_SOURCE:=daed-src-2026.08.03-5f7939adc12a.tar.gz PKG_SOURCE:=daed-src-2026.08.03-5f7939adc12a.tar.gz
PKG_SOURCE_URL:=https://github.com/kenzok8/openwrt-daede/releases/download/daed-src PKG_SOURCE_URL:=https://github.com/kenzok8/openwrt-daede/releases/download/daed-src
@@ -0,0 +1,24 @@
diff --git a/graphql/service/subscription/mutation_utils.go b/graphql/service/subscription/mutation_utils.go
index e01a342..c6bc3e1 100644
--- a/graphql/service/subscription/mutation_utils.go
+++ b/graphql/service/subscription/mutation_utils.go
@@ -171,6 +171,8 @@ func AutoUpdateVersionByIds(d *gorm.DB, ids []uint) (err error) {
return nil
}
+const scheduledUpdateTimeout = 3 * time.Minute
+
var (
schedulerCache = make(map[uint]*gocron.Scheduler)
schedulerMu sync.RWMutex
@@ -224,7 +226,9 @@ func AddUpdateScheduler(ctx context.Context, id uint) {
}
logrus.Info("Subscription " + tag + " update task enabled, with exp " + sub.CronExp)
_, err := s.Cron(sub.CronExp).Do(func() {
- if _, err := UpdateById(context.Background(), sub.ID); err != nil {
+ ctx, cancel := context.WithTimeout(context.Background(), scheduledUpdateTimeout)
+ defer cancel()
+ if _, err := UpdateById(ctx, sub.ID); err != nil {
logrus.Error(err)
}
})
+2 -2
View File
@@ -2,8 +2,8 @@
include $(TOPDIR)/rules.mk include $(TOPDIR)/rules.mk
PKG_VERSION:=1.0.0-r20250610 PKG_VERSION:=1.0.0-r20260804
PKG_RELEASE:=4 PKG_RELEASE:=5
LUCI_TITLE:=LuCI support for arcadia LUCI_TITLE:=LuCI support for arcadia
LUCI_PKGARCH:=all LUCI_PKGARCH:=all
@@ -24,7 +24,7 @@ s = m:section(TypedSection, "arcadia", translate("Setup"),
s.addremove=false s.addremove=false
s.anonymous=true s.anonymous=true
o = s:option(Value, "image", translate("Image")) o = s:option(Value, "image", translate("Docker Image"))
o.datatype = "string" o.datatype = "string"
o:value("", translate("Default")) o:value("", translate("Default"))
o:value("supermanito/arcadia:beta", "supermanito/arcadia:beta") o:value("supermanito/arcadia:beta", "supermanito/arcadia:beta")
+1 -1
View File
@@ -16,7 +16,7 @@ msgstr "项目基于 TypeScript 全栈开发,采用了众多前沿技术,前
msgid "The initial username and password are 'useradmin' and 'passwd' respectively." msgid "The initial username and password are 'useradmin' and 'passwd' respectively."
msgstr "初始的用户名和密码分别是'useradmin'和'passwd'。" msgstr "初始的用户名和密码分别是'useradmin'和'passwd'。"
msgid "Image" msgid "Docker Image"
msgstr "镜像" msgstr "镜像"
msgid "Default" msgid "Default"
+2 -2
View File
@@ -2,8 +2,8 @@
include $(TOPDIR)/rules.mk include $(TOPDIR)/rules.mk
PKG_VERSION:=1.0.3-r20240822 PKG_VERSION:=1.0.3-r20260804
PKG_RELEASE:=4 PKG_RELEASE:=5
LUCI_TITLE:=LuCI support for ChineseSubFinder LUCI_TITLE:=LuCI support for ChineseSubFinder
LUCI_PKGARCH:=all LUCI_PKGARCH:=all
@@ -28,7 +28,7 @@ o.rmempty = false
o.default = "19037" o.default = "19037"
o.datatype = "string" o.datatype = "string"
o = s:option(Value, "image_name", translate("Image").."<b>*</b>") o = s:option(Value, "image_name", translate("Docker Image").."<b>*</b>")
o.rmempty = false o.rmempty = false
o.datatype = "string" o.datatype = "string"
o:value("allanpk716/chinesesubfinder:latest-lite", "allanpk716/chinesesubfinder:latest-lite") o:value("allanpk716/chinesesubfinder:latest-lite", "allanpk716/chinesesubfinder:latest-lite")
@@ -10,6 +10,9 @@ msgstr "ChineseSubFinder是一个自动化中文字幕下载工具。"
msgid "Config path" msgid "Config path"
msgstr "配置文件路径" msgstr "配置文件路径"
msgid "Docker Image"
msgstr "镜像"
msgid "HTTP Port" msgid "HTTP Port"
msgstr "HTTP 端口" msgstr "HTTP 端口"
+2 -2
View File
@@ -2,8 +2,8 @@
include $(TOPDIR)/rules.mk include $(TOPDIR)/rules.mk
PKG_VERSION:=1.0.2-r20251011 PKG_VERSION:=1.0.2-r20260804
PKG_RELEASE:=4 PKG_RELEASE:=5
LUCI_TITLE:=LuCI support for CodeServer LUCI_TITLE:=LuCI support for CodeServer
LUCI_PKGARCH:=all LUCI_PKGARCH:=all
@@ -22,7 +22,7 @@ o = s:option(Value, "http_port", translate("HTTP Port").."<b>*</b>")
o.default = "8085" o.default = "8085"
o.datatype = "string" o.datatype = "string"
o = s:option(Value, "image_name", translate("Image").."<b>*</b>") o = s:option(Value, "image_name", translate("Docker Image").."<b>*</b>")
o.rmempty = false o.rmempty = false
o.datatype = "string" o.datatype = "string"
o:value("lscr.io/linuxserver/code-server:latest", "lscr.io/linuxserver/code-server:latest") o:value("lscr.io/linuxserver/code-server:latest", "lscr.io/linuxserver/code-server:latest")
@@ -10,6 +10,9 @@ msgstr "CodeServer 就一个在线版本的 VSCode,可以在线开发。"
msgid "Config path" msgid "Config path"
msgstr "配置文件路径" msgstr "配置文件路径"
msgid "Docker Image"
msgstr "镜像"
msgid "HTTP Port" msgid "HTTP Port"
msgstr "HTTP 端口" msgstr "HTTP 端口"
+1 -1
View File
@@ -9,7 +9,7 @@ LUCI_TITLE:=LuCI support for Timewol
LUCI_DEPENDS:=+etherwake LUCI_DEPENDS:=+etherwake
LUCI_PKGARCH:=all LUCI_PKGARCH:=all
PKG_VERSION:=1.1 PKG_VERSION:=1.1
PKG_RELEASE:=2 PKG_RELEASE:=3
include $(TOPDIR)/feeds/luci/luci.mk include $(TOPDIR)/feeds/luci/luci.mk
@@ -1,19 +0,0 @@
#!/bin/sh
[ ! -f "/usr/share/ucitrack/luci-app-luci_app_control_timewol.json" ] && {
cat > /usr/share/ucitrack/luci-app-luci_app_control_timewol.json << EEOF
{
"config": "luci_app_control_timewol",
"init": "luci_app_control_timewol"
}
EEOF
}
uci -q batch <<-EOF >/dev/null
delete ucitrack.@luci_app_control_timewol[-1]
add ucitrack luci_app_control_timewol
set ucitrack.@luci_app_control_timewol[-1].init=luci-app-control-timewol
commit ucitrack
EOF
rm -rf /tmp/luci-*cache
exit 0
+1 -1
View File
@@ -8,7 +8,7 @@ include $(TOPDIR)/rules.mk
LUCI_TITLE:=LuCI support for Webrestriction LUCI_TITLE:=LuCI support for Webrestriction
LUCI_PKGARCH:=all LUCI_PKGARCH:=all
PKG_VERSION:=1.1 PKG_VERSION:=1.1
PKG_RELEASE:=2 PKG_RELEASE:=3
include $(TOPDIR)/feeds/luci/luci.mk include $(TOPDIR)/feeds/luci/luci.mk
@@ -1,19 +0,0 @@
#!/bin/sh
[ ! -f "/usr/share/ucitrack/luci-app-luci_app_control_webrestriction.json" ] && {
cat > /usr/share/ucitrack/luci-app-luci_app_control_webrestriction.json << EEOF
{
"config": "luci_app_control_webrestriction",
"init": "luci_app_control_webrestriction"
}
EEOF
}
uci -q batch <<-EOF >/dev/null
delete ucitrack.@luci_app_control_webrestriction[-1]
add ucitrack luci_app_control_webrestriction
set ucitrack.@luci_app_control_webrestriction[-1].init=luci-app-control-webrestriction
commit ucitrack
EOF
rm -rf /tmp/luci-*cache
exit 0
+1 -1
View File
@@ -9,7 +9,7 @@ LUCI_TITLE:=LuCI support for Weburl
LUCI_DEPENDS:=+iptables-mod-filter +kmod-ipt-filter LUCI_DEPENDS:=+iptables-mod-filter +kmod-ipt-filter
LUCI_PKGARCH:=all LUCI_PKGARCH:=all
PKG_VERSION:=1.1 PKG_VERSION:=1.1
PKG_RELEASE:=2 PKG_RELEASE:=3
include $(TOPDIR)/feeds/luci/luci.mk include $(TOPDIR)/feeds/luci/luci.mk
@@ -1,19 +0,0 @@
#!/bin/sh
[ ! -f "/usr/share/ucitrack/luci-app-luci_app_control_weburl.json" ] && {
cat > /usr/share/ucitrack/luci-app-luci_app_control_weburl.json << EEOF
{
"config": "luci_app_control_weburl",
"init": "luci_app_control_weburl"
}
EEOF
}
uci -q batch <<-EOF >/dev/null
delete ucitrack.@luci_app_control_weburl[-1]
add ucitrack luci_app_control_weburl
set ucitrack.@luci_app_control_weburl[-1].init=luci-app-control-weburl
commit ucitrack
EOF
rm -rf /tmp/luci-*cache
exit 0
+2 -2
View File
@@ -2,8 +2,8 @@
include $(TOPDIR)/rules.mk include $(TOPDIR)/rules.mk
PKG_VERSION:=1.2.0-r20250625 PKG_VERSION:=1.2.0-r20260804
PKG_RELEASE:=4 PKG_RELEASE:=5
LUCI_TITLE:=LuCI support for demon LUCI_TITLE:=LuCI support for demon
LUCI_PKGARCH:=all LUCI_PKGARCH:=all
+1 -1
View File
@@ -21,7 +21,7 @@ o = s:option(Value, "port", translate("Port").."<b>*</b>")
o.default = "18888" o.default = "18888"
o.datatype = "port" o.datatype = "port"
o = s:option(Value, "image_name", translate("Image").."<b>*</b>") o = s:option(Value, "image_name", translate("Docker Image").."<b>*</b>")
o.rmempty = false o.rmempty = false
o.datatype = "string" o.datatype = "string"
o:value("images-cluster.xycloud.com/wxedge/amd64-wxedge:3.5.1-CTWXKS1748570956", "images-cluster.xycloud.com/wxedge/amd64-wxedge:3.5.1-CTWXKS1748570956") o:value("images-cluster.xycloud.com/wxedge/amd64-wxedge:3.5.1-CTWXKS1748570956", "images-cluster.xycloud.com/wxedge/amd64-wxedge:3.5.1-CTWXKS1748570956")
+3
View File
@@ -7,6 +7,9 @@ msgstr "容器魔王"
msgid "Cache path" msgid "Cache path"
msgstr "缓存文件路径" msgstr "缓存文件路径"
msgid "Docker Image"
msgstr "镜像"
msgid "Service Status" msgid "Service Status"
msgstr "服务状态" msgstr "服务状态"
+2 -3
View File
@@ -2,9 +2,8 @@
include $(TOPDIR)/rules.mk include $(TOPDIR)/rules.mk
# Updated: 2025-10-09 PKG_VERSION:=1.0.0-r20260804
PKG_VERSION:=1.0.0-r20251009 PKG_RELEASE:=5
PKG_RELEASE:=4
LUCI_TITLE:=LuCI support for DPanel LUCI_TITLE:=LuCI support for DPanel
LUCI_PKGARCH:=all LUCI_PKGARCH:=all
+1 -1
View File
@@ -35,7 +35,7 @@ o = s:option(Value, "port", translate("Port").."<b>*</b>")
o.default = "8807" o.default = "8807"
o.datatype = "port" o.datatype = "port"
o = s:option(Value, "image_name", translate("Image").."<b>*</b>") o = s:option(Value, "image_name", translate("Docker Image").."<b>*</b>")
o.rmempty = false o.rmempty = false
o.datatype = "string" o.datatype = "string"
o:value("dpanel/dpanel:lite", "dpanel/dpanel:lite") o:value("dpanel/dpanel:lite", "dpanel/dpanel:lite")
+1 -1
View File
@@ -10,7 +10,7 @@ msgstr "DPanel — 轻量级仪表盘服务。"
msgid "DPanel" msgid "DPanel"
msgstr "DPanel" msgstr "DPanel"
msgid "Image" msgid "Docker Image"
msgstr "镜像" msgstr "镜像"
msgid "Config path" msgid "Config path"
+2 -2
View File
@@ -2,8 +2,8 @@
include $(TOPDIR)/rules.mk include $(TOPDIR)/rules.mk
PKG_VERSION:=1.0.3-r20240822 PKG_VERSION:=1.0.3-r20260804
PKG_RELEASE:=4 PKG_RELEASE:=5
LUCI_TITLE:=LuCI support for Emby LUCI_TITLE:=LuCI support for Emby
LUCI_PKGARCH:=all LUCI_PKGARCH:=all
+1 -1
View File
@@ -27,7 +27,7 @@ o.default = "8097"
o.datatype = "port" o.datatype = "port"
o:depends("hostnet", 0) o:depends("hostnet", 0)
o = s:option(Value, "image_name", translate("Image").."<b>*</b>") o = s:option(Value, "image_name", translate("Docker Image").."<b>*</b>")
o.rmempty = false o.rmempty = false
o.datatype = "string" o.datatype = "string"
o:value("emby/embyserver", "emby/embyserver") o:value("emby/embyserver", "emby/embyserver")
+3
View File
@@ -10,6 +10,9 @@ msgstr "Emby 是一个多媒体串流平台。"
msgid "Config path" msgid "Config path"
msgstr "配置文件路径" msgstr "配置文件路径"
msgid "Docker Image"
msgstr "镜像"
msgid "HTTP Port" msgid "HTTP Port"
msgstr "HTTP 端口" msgstr "HTTP 端口"
+2 -2
View File
@@ -2,8 +2,8 @@
include $(TOPDIR)/rules.mk include $(TOPDIR)/rules.mk
PKG_VERSION:=1.0.0-r20240822 PKG_VERSION:=1.0.0-r20260804
PKG_RELEASE:=4 PKG_RELEASE:=5
LUCI_TITLE:=LuCI support for Excalidraw LUCI_TITLE:=LuCI support for Excalidraw
LUCI_PKGARCH:=all LUCI_PKGARCH:=all
@@ -35,7 +35,7 @@ o = s:option(Value, "port", translate("Port").."<b>*</b>")
o.default = "8090" o.default = "8090"
o.datatype = "port" o.datatype = "port"
o = s:option(Value, "image_ver", translate("Image").."<b>*</b>") o = s:option(Value, "image_ver", translate("Docker Image").."<b>*</b>")
o.rmempty = false o.rmempty = false
o.datatype = "string" o.datatype = "string"
o.default = "v0.13.0" o.default = "v0.13.0"
@@ -13,6 +13,9 @@ msgstr "Excalidraw 是支持多人协同的私有化在线画板工具。"
msgid "Config path" msgid "Config path"
msgstr "配置文件路径" msgstr "配置文件路径"
msgid "Docker Image"
msgstr "镜像"
msgid "Port" msgid "Port"
msgstr "端口" msgstr "端口"
+1
View File
@@ -20,6 +20,7 @@ PKG_NAME:=luci-app-fchomo
define Package/luci-app-fchomo/conffiles define Package/luci-app-fchomo/conffiles
/etc/config/fchomo /etc/config/fchomo
/etc/fchomo/certs/ /etc/fchomo/certs/
/etc/fchomo/node/
/etc/fchomo/provider/ /etc/fchomo/provider/
/etc/fchomo/ruleset/ /etc/fchomo/ruleset/
/etc/fchomo/resources/ /etc/fchomo/resources/
@@ -204,7 +204,9 @@ const outbound_type = [
['hysteria2', _('Hysteria2') + ' - ' + _('UDP')], ['hysteria2', _('Hysteria2') + ' - ' + _('UDP')],
['shadowquic', _('ShadowQUIC') + ' - ' + _('UDP')], ['shadowquic', _('ShadowQUIC') + ' - ' + _('UDP')],
['trusttunnel', _('TrustTunnel') + ' - ' + _('TCP/UDP')], ['trusttunnel', _('TrustTunnel') + ' - ' + _('TCP/UDP')],
['zerotier', _('ZeroTier') + ' - ' + _('UDP') + ' - ' + _('L2')], // Endpoint
['wireguard', _('WireGuard') + ' - ' + _('UDP')], // Endpoint ['wireguard', _('WireGuard') + ' - ' + _('UDP')], // Endpoint
['tailscale', _('Tailscale') + ' - ' + _('UDP')], // Endpoint
['masque', _('Masque') + ' - ' + _('UDP')], // Endpoint // https://blog.cloudflare.com/post-quantum-warp/ ['masque', _('Masque') + ' - ' + _('UDP')], // Endpoint // https://blog.cloudflare.com/post-quantum-warp/
['ssh', _('SSH') + ' - ' + _('TCP')] ['ssh', _('SSH') + ' - ' + _('TCP')]
]; ];
@@ -1879,6 +1881,18 @@ function validateUrl(section_id, value) {
return true; return true;
} }
function validateHexstr(length, section_id, value) {
if (!value)
return true;
length /= 4; // Convert bits to hex characters
const regexp = new RegExp(`^[0-9a-fA-F]{${length}}$`);
if (!value.match(regexp))
return _('Expecting: %s').format(_('valid hex string with %d characters').format(length));
return true;
}
function validateBase64Key(length, section_id, value) { function validateBase64Key(length, section_id, value) {
/* Thanks to luci-proto-wireguard */ /* Thanks to luci-proto-wireguard */
if (value) if (value)
@@ -1986,15 +2000,15 @@ function lsDir(type) {
}); });
} }
function readFile(type, filename) { function readFile(type, filename, isbinary) {
const callReadFile = rpc.declare({ const callReadFile = rpc.declare({
object: 'luci.fchomo', object: 'luci.fchomo',
method: 'file_read', method: 'file_read',
params: ['type', 'filename'], params: ['type', 'filename', 'isbinary'],
expect: { '': {} } expect: { '': {} }
}); });
return L.resolveDefault(callReadFile(type, filename), {}).then((res) => { return L.resolveDefault(callReadFile(type, filename, isbinary), {}).then((res) => {
if (res.content ?? true) { if (res.content ?? true) {
return res.content; return res.content;
} else } else
@@ -2002,15 +2016,15 @@ function readFile(type, filename) {
}); });
} }
function writeFile(type, filename, content) { function writeFile(type, filename, content, isbinary) {
const callWriteFile = rpc.declare({ const callWriteFile = rpc.declare({
object: 'luci.fchomo', object: 'luci.fchomo',
method: 'file_write', method: 'file_write',
params: ['type', 'filename', 'content'], params: ['type', 'filename', 'content', 'isbinary'],
expect: { '': {} } expect: { '': {} }
}); });
return L.resolveDefault(callWriteFile(type, filename, content), {}).then((res) => { return L.resolveDefault(callWriteFile(type, filename, content, isbinary), {}).then((res) => {
if (res.result) { if (res.result) {
return res.result; return res.result;
} else } else
@@ -2182,6 +2196,7 @@ return baseclass.extend({
validateUUID, validateUUID,
validateUrl, validateUrl,
// validate with bind this // validate with bind this
validateHexstr,
validateBase64Key, validateBase64Key,
validateMTLSClientAuth, validateMTLSClientAuth,
validatePresetIDs, validatePresetIDs,
@@ -232,6 +232,19 @@ return view.extend({
ss.hm_prefmt = hm.glossary[ss.sectiontype].prefmt; ss.hm_prefmt = hm.glossary[ss.sectiontype].prefmt;
ss.hm_field = hm.glossary[ss.sectiontype].field; ss.hm_field = hm.glossary[ss.sectiontype].field;
ss.hm_lowcase_only = true; ss.hm_lowcase_only = true;
/* Remove idle files start */
ss.renderSectionAdd = function(/* ... */) {
let el = hm.GridSection.prototype.renderSectionAdd.apply(this, arguments);
el.appendChild(E('button', {
'class': 'cbi-button cbi-button-add',
'title': _('Remove idles'),
'click': ui.createHandlerFn(this, hm.handleRemoveIdles)
}, [ _('Remove idles') ]));
return el;
}
/* Remove idle files end */
ss.tab('field_general', _('General fields')); ss.tab('field_general', _('General fields'));
ss.tab('field_plugin', _('Plugin fields')); ss.tab('field_plugin', _('Plugin fields'));
@@ -261,12 +274,12 @@ return view.extend({
so = ss.taboption('field_general', form.Value, 'server', _('Server address')); so = ss.taboption('field_general', form.Value, 'server', _('Server address'));
so.datatype = 'host'; so.datatype = 'host';
so.rmempty = false; so.rmempty = false;
so.depends({type: /^(rematch|direct)$/, '!reverse': true}); so.depends({type: /^(rematch|direct|zerotier|tailscale)$/, '!reverse': true});
so = ss.taboption('field_general', form.Value, 'port', _('Port')); so = ss.taboption('field_general', form.Value, 'port', _('Port'));
so.datatype = 'port'; so.datatype = 'port';
so.rmempty = false; so.rmempty = false;
so.depends({type: /^(rematch|direct|mieru)$/, '!reverse': true}); so.depends({type: /^(rematch|direct|mieru|zerotier|tailscale)$/, '!reverse': true});
/* Rematch fields */ /* Rematch fields */
// https://github.com/MetaCubeX/mihomo/pull/2862 // https://github.com/MetaCubeX/mihomo/pull/2862
@@ -782,15 +795,131 @@ return view.extend({
so.depends('type', 'trusttunnel'); so.depends('type', 'trusttunnel');
so.modalonly = true; so.modalonly = true;
/* ZeroTier fields */
so = ss.taboption('field_general', form.Value, 'zerotier_network_id', 'Network ID');
so.placeholder = '0123456789abcdef';
so.validate = L.bind(hm.validateHexstr, so, 64);
so.rmempty = false;
so.depends('type', 'zerotier');
so.modalonly = true;
so = ss.taboption('field_general', form.Value, 'zerotier_trace_target', _('Remote trace target'),
_('%s of the %s used to receiving remote debugging log.').format('Node ID', 'Node'));
so.placeholder = '0123456789';
so.validate = L.bind(hm.validateHexstr, so, 40);
so.depends('type', 'zerotier');
so.modalonly = true;
so = ss.taboption('field_general', form.ListValue, 'zerotier_trace_level', _('Remote trace level'));
so.value('0', _('Normal'));
so.value('10', _('Verbose'));
so.value('15', _('Rules'));
so.value('20', _('Debug'));
so.value('30', _('Insane'));
so.depends({zerotier_trace_target: /.+/});
so.modalonly = true;
so = ss.taboption('field_general', form.Value, 'zerotier_physical_mtu', _('Physical MTU'),
_('ZeroTier UDP payload MTU.'));
so.datatype = 'range(510, 10324)';
so.placeholder = '1432';
so.depends('type', 'zerotier');
so.modalonly = true;
so = ss.taboption('field_general', form.Flag, 'zerotier_low_bandwidth', _('Low Bandwidth Mode'),
_('Reduces background traffic and network configuration refresh frequency.'));
so.default = so.disabled;
so.depends('type', 'zerotier');
so.modalonly = true;
so = ss.taboption('field_general', form.Flag, 'zerotier_encrypted_hello', _('Encrypted Hello'),
_('Use protocol-13 extended encryption for outgoing HELLO packets.'));
so.default = so.disabled;
so.depends('type', 'zerotier');
so.modalonly = true;
so = ss.taboption('field_general', form.RichListValue, 'zerotier_fallback_mode', _('Fallback mode'));
so.value('auto', _('Auto'), _('Uses relay after UDP direct failure.'));
so.value('force', _('Force'), _('Uses TCP relay only.'));
so.value('disable', _('Disable'), _('Turns TCP relay off.'));
so.depends('type', 'zerotier');
so.modalonly = true;
so = ss.taboption('field_general', form.Value, 'zerotier_fallback_relay', _('Fallback relay'));
so.datatype = 'hostport';
so.placeholder = '204.80.128.1:443';
so.depends('type', 'zerotier');
so.modalonly = true;
so = ss.taboption('field_general', form.TextValue, 'zerotier_planet_file', 'Planet file',
_('Used to replace the built-in official %s.').format('<code>Earth</code> Planet file'));
so.placeholder = _('Add the base64 text of the planet file here.');
so.load = function(section_id) {
const vaule = form.TextValue.prototype.load.apply(this, arguments);
if (vaule)
return L.resolveDefault(hm.readFile(this.section.sectiontype, section_id + '/planet', '1'), '');
else
return vaule;
}
so.write = function(section_id, formvalue) {
form.TextValue.prototype.write.call(this, section_id, '1');
return hm.writeFile.call(this, this.section.sectiontype, section_id + '/planet', formvalue, '1');
}
so.remove = function(section_id) {
form.TextValue.prototype.remove.apply(this, arguments);
return hm.removeFile.call(this, this.section.sectiontype, section_id + '/planet');
}
so.depends('type', 'zerotier');
so.modalonly = true;
so = ss.taboption('field_general', form.DynamicList, 'zerotier_orbit', 'Orbit',
_('Join %s. Format: <code>%s</code>').format('Moons', '000000<10-digit Moon World ID>:<10-digit Node ID of one Moon Root server>...'));
so.placeholder = '000000deadbeef00:deadbeef00:deadbeef11';
so.validate = function(section_id, value) {
if (!value)
return true;
if (!/^[0-9a-fA-F]{16}(:[0-9a-fA-F]{10})+$/.test(value))
return _('Expecting: %s').format(_('Valid 16-digit Moon World ID and 10-digit Node ID of Moon Root server.'));
return true;
}
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 */ /* WireGuard fields */
so = ss.taboption('field_general', form.Value, 'wireguard_private_key', _('Private key'), so = ss.taboption('field_general', hm.GenValue, 'wireguard_private_key', _('Private key'),
_('WireGuard requires base64-encoded private keys.')); _('WireGuard requires base64-encoded private keys.'));
so.hm_options = {
type: 'wg-keypair',
callback: function(result) {
return [
[this.option, result.private_key],
['wireguard_public_key', result.public_key]
]
}
}
so.password = true; so.password = true;
so.validate = L.bind(hm.validateBase64Key, so, 44); so.validate = L.bind(hm.validateBase64Key, so, 44);
so.rmempty = false; so.rmempty = false;
so.depends('type', 'wireguard'); so.depends('type', 'wireguard');
so.modalonly = true; so.modalonly = true;
so = ss.taboption('field_general', hm.CopyValue, 'wireguard_public_key', _('Public key'));
so.depends('type', 'wireguard');
so.modalonly = true;
so = ss.taboption('field_general', form.Value, 'wireguard_peer_public_key', _('Peer public key'), so = ss.taboption('field_general', form.Value, 'wireguard_peer_public_key', _('Peer public key'),
_('WireGuard peer public key.')); _('WireGuard peer public key.'));
so.validate = L.bind(hm.validateBase64Key, so, 44); so.validate = L.bind(hm.validateBase64Key, so, 44);
@@ -823,6 +952,45 @@ return view.extend({
so.depends('type', 'wireguard'); so.depends('type', 'wireguard');
so.modalonly = true; so.modalonly = true;
/* Tailscale fields */
so = ss.taboption('field_general', form.Value, 'tailscale_hostname', _('%s Hostname').format(_('Tailscale')));
so.datatype = 'hostname';
so.depends('type', 'tailscale');
so.modalonly = true;
so = ss.taboption('field_general', form.Value, 'tailscale_control_url', _('Control server'),
_('Custom %s control server.').format(_('Headscale/Tailscale')));
so.validate = hm.validateUrl;
so.depends('type', 'tailscale');
so.modalonly = true;
so = ss.taboption('field_general', form.Value, 'tailscale_auth_key', _('Authentication key'));
so.password = true;
so.depends('type', 'tailscale');
so.modalonly = true;
so = ss.taboption('field_general', form.Flag, 'tailscale_ephemeral', _('As ephemeral node'));
so.default = so.disabled;
so.depends('type', 'tailscale');
so.modalonly = true;
so = ss.taboption('field_general', form.Flag, 'tailscale_accept_routes', _('Accept routes'),
_('Whether to accept routes advertised by other nodes.'));
so.default = so.disabled;
so.depends('type', 'tailscale');
so.modalonly = true;
so = ss.taboption('field_general', form.Value, 'tailscale_exit_node', _('Exit node'));
so.datatype = "or(ipaddr(1), 'auto:any')";
so.depends('type', 'tailscale');
so.modalonly = true;
so = ss.taboption('field_general', form.Flag, 'tailscale_exit_node_allow_lan_access', _('Allow LAN access'),
_('Allow access to the local LAN via the exit node.'));
so.default = so.disabled;
so.depends({tailscale_exit_node: /.+/});
so.modalonly = true;
/* Masque fields */ /* Masque fields */
so = ss.taboption('field_general', form.Value, 'masque_private_key', _('Private key'), so = ss.taboption('field_general', form.Value, 'masque_private_key', _('Private key'),
_('Base64 encoded ECDSA private key on the NIST P-256 curve.')); _('Base64 encoded ECDSA private key on the NIST P-256 curve.'));
@@ -832,7 +1000,7 @@ return view.extend({
so.depends('type', 'masque'); so.depends('type', 'masque');
so.modalonly = true; so.modalonly = true;
so = ss.taboption('field_general', form.Value, 'masque_endpoint_public_key', _('Endpoint public key'), so = ss.taboption('field_general', form.Value, 'masque_endpoint_public_key', _('Server public key'),
_('Base64 encoded ECDSA public key on the NIST P-256 curve.')); _('Base64 encoded ECDSA public key on the NIST P-256 curve.'));
so.validate = L.bind(hm.validateBase64Key, so, 124); so.validate = L.bind(hm.validateBase64Key, so, 124);
so.rmempty = false; so.rmempty = false;
@@ -887,6 +1055,7 @@ return view.extend({
}) })
so.depends({type: /^(tuic|shadowquic|trusttunnel)$/}); so.depends({type: /^(tuic|shadowquic|trusttunnel)$/});
so.depends({type: 'masque', masque_network: /^(|h3-l4proxy)$/}); so.depends({type: 'masque', masque_network: /^(|h3-l4proxy)$/});
so.depends({type: 'zerotier', zerotier_ipstack: /^(auto|mips)$/}); // not empty not gvisor
so.modalonly = true; so.modalonly = true;
so = ss.taboption('field_general', form.ListValue, 'bbr_profile', _('BBR profile')); so = ss.taboption('field_general', form.ListValue, 'bbr_profile', _('BBR profile'));
@@ -914,7 +1083,7 @@ return view.extend({
so = ss.taboption('field_general', form.Flag, 'udp', _('UDP')); so = ss.taboption('field_general', form.Flag, 'udp', _('UDP'));
so.default = so.disabled; so.default = so.disabled;
so.depends({type: /^(rematch|direct|socks5|ss|mieru|vmess|vless|trojan|anytls|trusttunnel|wireguard|masque)$/}); so.depends({type: /^(rematch|direct|socks5|ss|mieru|vmess|vless|trojan|anytls|trusttunnel|zerotier|wireguard|tailscale|masque)$/});
so.depends({type: 'snell', snell_version: /^(3|4|5)$/}); so.depends({type: 'snell', snell_version: /^(3|4|5)$/});
so.modalonly = true; so.modalonly = true;
@@ -1124,7 +1293,7 @@ return view.extend({
so.default = so.disabled; so.default = so.disabled;
so.validate = function(section_id, value) { so.validate = function(section_id, value) {
const type = this.section.getOption('type').formvalue(section_id); const type = this.section.getOption('type').formvalue(section_id);
let tls = this.section.getUIElement(section_id, 'tls').node.querySelector('input'); const tls = this.getUIElement(section_id).node.querySelector('input');
// Force enabled // Force enabled
if (['trojan', 'anytls', 'tuic', 'hysteria', 'hysteria2', 'shadowquic', 'trusttunnel', 'masque'].includes(type)) { if (['trojan', 'anytls', 'tuic', 'hysteria', 'hysteria2', 'shadowquic', 'trusttunnel', 'masque'].includes(type)) {
@@ -1315,6 +1484,87 @@ return view.extend({
// @VMess-TLSmirror fields // @VMess-TLSmirror fields
/* VPN fields */ /* VPN fields */
so = ss.taboption('field_vpn', form.Flag, 'auto_firewall', _('Firewall'),
_('Auto configure firewall'));
so.default = so.enabled;
so.validate = function(section_id, value) {
const primary_port = this.section.getOption('zerotier_primary_port').formvalue(section_id);
const secondary_port = this.section.getOption('zerotier_secondary_port').formvalue(section_id);
const auto_firewall = this.getUIElement(section_id).node.querySelector('input');
// Force disabled
if ((!primary_port || primary_port <= 0) && (!secondary_port || secondary_port <= 0)) {
auto_firewall.checked = false;
auto_firewall.disabled = true;
} else
auto_firewall.removeAttribute('disabled');
return true;
}
so.depends('type', 'zerotier');
so.modalonly = true;
so = ss.taboption('field_vpn', form.Value, 'zerotier_primary_port', _('Listen port') + ' (%s)'.format(_('Primary')),
_('%s UDP port. <code>0</code> selects an available port.').format(_('Primary')));
so.datatype = 'port';
so.placeholder = '9993';
so.load = function(section_id) {
const listen_port = this.map.data.get(this.section.config, section_id, 'zerotier_listen_port');
const value = [
this.map.data.get(this.section.config, section_id, 'zerotier_primary_port'),
this.map.data.get(this.section.config, section_id, 'zerotier_secondary_port')
].filter(Boolean).join(',');
if (listen_port !== value) {
uci.set(this.section.config, section_id, 'zerotier_listen_port', value);
uci.save();
}
return form.Value.prototype.load.apply(this, arguments);
}
so.write = function(section_id, formvalue) {
uci.set(this.section.config, section_id, 'zerotier_listen_port', [
this.section.getOption('zerotier_primary_port').formvalue(section_id),
this.section.getOption('zerotier_secondary_port').formvalue(section_id)
].filter(Boolean).join(','));
return form.Value.prototype[formvalue ? 'write' : 'remove'].apply(this, arguments);
}
so.remove = so.write;
so.depends('type', 'zerotier');
so.modalonly = true;
so = ss.taboption('field_vpn', form.Value, 'zerotier_secondary_port', _('Listen port') + ' (%s)'.format(_('Secondary')),
_('%s UDP port. <code>0</code> selects an available port.').format(_('Secondary')) + '</br>' +
_('<code>-1</code> disables it.'));
so.datatype = 'or(port, -1)';
so.placeholder = '0';
so.load = function(section_id) {
const listen_port = this.map.data.get(this.section.config, section_id, 'zerotier_listen_port');
const value = [
this.map.data.get(this.section.config, section_id, 'zerotier_primary_port'),
this.map.data.get(this.section.config, section_id, 'zerotier_secondary_port')
].filter(Boolean).join(',');
if (listen_port !== value) {
uci.set(this.section.config, section_id, 'zerotier_listen_port', value);
uci.save();
}
return form.Value.prototype.load.apply(this, arguments);
}
so.write = function(section_id, formvalue) {
uci.set(this.section.config, section_id, 'zerotier_listen_port', [
this.section.getOption('zerotier_primary_port').formvalue(section_id),
this.section.getOption('zerotier_secondary_port').formvalue(section_id)
].filter(Boolean).join(','));
return form.Value.prototype[formvalue ? 'write' : 'remove'].apply(this, arguments);
}
so.remove = so.write;
so.depends('type', 'zerotier');
so.modalonly = true;
so = ss.taboption('field_vpn', form.Value, 'endpoint_ip', _('Virtual address'), so = ss.taboption('field_vpn', form.Value, 'endpoint_ip', _('Virtual address'),
_('The %s address used by local machine in the %s network.').format(_('IPv4'), _('VPN'))); _('The %s address used by local machine in the %s network.').format(_('IPv4'), _('VPN')));
so.datatype = 'or(ip4addr(1), cidr4)'; so.datatype = 'or(ip4addr(1), cidr4)';
@@ -1374,6 +1624,9 @@ return view.extend({
let def_mtu; let def_mtu;
switch (type) { switch (type) {
case 'zerotier':
def_mtu = '1400';
break;
case 'wireguard': case 'wireguard':
def_mtu = '1408'; def_mtu = '1408';
break; break;
@@ -1385,14 +1638,14 @@ return view.extend({
return true; return true;
} }
so.depends('type', 'wireguard'); so.depends({type: /^(zerotier|wireguard)$/});
so.depends({type: 'masque', masque_network: /^(|h2)$/}); so.depends({type: 'masque', masque_network: /^(|h2)$/});
so.modalonly = true; so.modalonly = true;
so = ss.taboption('field_vpn', form.Flag, 'endpoint_remote_dns_resolve', _('Remote DNS resolve'), so = ss.taboption('field_vpn', form.Flag, 'endpoint_remote_dns_resolve', _('Remote DNS resolve'),
_('Force DNS remote resolution.')); _('Force DNS remote resolution.'));
so.default = so.disabled; so.default = so.disabled;
so.depends({type: /^(wireguard|masque)$/}); so.depends({type: /^(zerotier|wireguard|masque)$/});
so.modalonly = true; so.modalonly = true;
so = ss.taboption('field_vpn', form.DynamicList, 'endpoint_dns', _('DNS server')); so = ss.taboption('field_vpn', form.DynamicList, 'endpoint_dns', _('DNS server'));
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+11 -1
View File
@@ -47,11 +47,16 @@ opmc() { # @less_25_12
# add_firewall # add_firewall
add_firewall() { add_firewall() {
local enabled auto_firewall listen listen_port local enabled auto_firewall listen listen_port type
config_get_bool enabled "$1" "enabled" "1" config_get_bool enabled "$1" "enabled" "1"
config_get_bool auto_firewall "$1" "auto_firewall" "1" config_get_bool auto_firewall "$1" "auto_firewall" "1"
config_get listen "$1" "listen" "::" config_get listen "$1" "listen" "::"
config_get listen_port "$1" "listen_port" config_get listen_port "$1" "listen_port"
config_get type "$1" "type"
local "${type}_listen_port"
config_get "${type}_listen_port" "$1" "${type}_listen_port"
eval "listen_port=\"$listen_port,\$${type}_listen_port\""
[ "$enabled" = "0" ] && return 0 [ "$enabled" = "0" ] && return 0
[ "$auto_firewall" = "0" ] && return 0 [ "$auto_firewall" = "0" ] && return 0
@@ -366,6 +371,7 @@ start_service() {
json_add_array firewall json_add_array firewall
# meta l4proto %s th dport %s counter accept comment "!%s: accept server instance [%s]" # meta l4proto %s th dport %s counter accept comment "!%s: accept server instance [%s]"
config_foreach add_firewall "inbound" config_foreach add_firewall "inbound"
config_foreach add_firewall "node"
json_close_array json_close_array
procd_close_data procd_close_data
@@ -454,6 +460,10 @@ stop_service() {
[ -z "$1" -o "$1" = "mihomo-c" ] && stop_client [ -z "$1" -o "$1" = "mihomo-c" ] && stop_client
# Server # Server
[ -z "$1" -o "$1" = "mihomo-s" ] && stop_server [ -z "$1" -o "$1" = "mihomo-s" ] && stop_server
# Directory
#find "$RUN_DIR"/* -maxdepth 0 -type d -exec rm -rf "{}" \;
# Resources
rm -rf "$RUN_DIR"/*.lock
# Setup firewall # Setup firewall
echo 2>"/dev/null" > "$RUN_DIR/fchomo_pre.nft" echo 2>"/dev/null" > "$RUN_DIR/fchomo_pre.nft"
return 0 return 0
@@ -1,7 +1,7 @@
#!/bin/sh #!/bin/sh
# Initialize necessary directories # Initialize necessary directories
for d in certs provider ruleset resources templates; do for d in certs node provider ruleset resources templates; do
mkdir -p "/etc/fchomo/$d/" 2>/dev/null mkdir -p "/etc/fchomo/$d/" 2>/dev/null
done done
@@ -514,12 +514,14 @@ uci.foreach(uciconf, ucinode, (cfg) => {
"target-rematch-name": cfg.target_rematch_name, "target-rematch-name": cfg.target_rematch_name,
"target-sub-rule": cfg.target_sub_rule, "target-sub-rule": cfg.target_sub_rule,
/* HTTP / SOCKS / Shadowsocks / VMess / VLESS / Trojan / TUIC / hysteria2 */ /* HTTP / SOCKS / Shadowsocks / VMess / VLESS / Trojan / TUIC / hysteria2 / ZeroTier / Tailscale / Masque */
username: cfg.username, username: cfg.username,
uuid: cfg.vmess_uuid || cfg.uuid, uuid: cfg.vmess_uuid || cfg.uuid,
cipher: cfg.vmess_chipher || cfg.shadowsocks_chipher, cipher: cfg.vmess_chipher || cfg.shadowsocks_chipher,
password: cfg.shadowsocks_password || cfg.password, password: cfg.shadowsocks_password || cfg.password,
headers: cfg.headers ? json(cfg.headers) : null, headers: cfg.headers ? json(cfg.headers) : null,
network: cfg.zerotier_network_id || cfg.masque_network || null,
"state-dir": `${HM_DIR}/${ucinode}/${cfg['.name']}`,
/* Shadowsocks */ /* Shadowsocks */
@@ -627,14 +629,50 @@ uci.foreach(uciconf, ucinode, (cfg) => {
"health-check": cfg.type === 'trusttunnel' ? (cfg.trusttunnel_health_check === '0' ? false : true) : null, "health-check": cfg.type === 'trusttunnel' ? (cfg.trusttunnel_health_check === '0' ? false : true) : null,
quic: strToBool(cfg.trusttunnel_quic), quic: strToBool(cfg.trusttunnel_quic),
/* ZeroTier */
"primary-port": strToInt(cfg.zerotier_primary_port),
"secondary-port": strToInt(cfg.zerotier_secondary_port),
"physical-mtu": strToInt(cfg.zerotier_physical_mtu) || null,
"tcp-fallback-mode": cfg.zerotier_fallback_mode,
"tcp-fallback-relay": cfg.zerotier_fallback_relay,
"remote-trace-target": cfg.zerotier_trace_target,
"remote-trace-level": strToInt(cfg.zerotier_trace_level),
"low-bandwidth": strToBool(cfg.zerotier_low_bandwidth),
"encrypted-hello": strToBool(cfg.zerotier_encrypted_hello),
//planet: `${HM_DIR}/${ucinode}/${cfg['.name']}/planet`,
...(isEmpty(cfg.zerotier_orbit) ? {} : {
orbit: map([0], () => {
const orbits = [];
for (let orbit in cfg.zerotier_orbit) {
orbit = split(orbit, ':');
const world = shift(orbit);
for (let seed in orbit)
push(orbits, {world: world, seed: seed});
}
return orbits;
})[0]
}),
"ip-stack": cfg.zerotier_ipstack ? {
mode: cfg.zerotier_ipstack,
"congestion-controller": replace(cfg.congestion_controller, 'new_reno', 'reno')
} : null,
/* WireGuard */ /* WireGuard */
"pre-shared-key": cfg.wireguard_pre_shared_key, "pre-shared-key": cfg.wireguard_pre_shared_key,
"allowed-ips": cfg.wireguard_allowed_ips, "allowed-ips": cfg.wireguard_allowed_ips,
reserved: cfg.wireguard_reserved, reserved: cfg.wireguard_reserved,
"persistent-keepalive": strToInt(cfg.wireguard_persistent_keepalive), "persistent-keepalive": strToInt(cfg.wireguard_persistent_keepalive),
/* Tailscale */
hostname: cfg.tailscale_hostname,
"auth-key": cfg.tailscale_auth_key,
"control-url": cfg.tailscale_control_url,
ephemeral: strToBool(cfg.tailscale_ephemeral),
"accept-routes": strToBool(cfg.tailscale_accept_routes),
"exit-node": cfg.tailscale_exit_node,
"exit-node-allow-lan-access": strToBool(cfg.tailscale_exit_node_allow_lan_access),
/* Masque */ /* Masque */
network: cfg.masque_network || null,
/* SSH */ /* SSH */
"private-key-passphrase": cfg.ssh_priv_key_passphrase, "private-key-passphrase": cfg.ssh_priv_key_passphrase,
@@ -645,7 +683,7 @@ uci.foreach(uciconf, ucinode, (cfg) => {
"udp-over-stream": strToBool(cfg.shadowquic_udp_over_stream || cfg.tuic_udp_over_stream), "udp-over-stream": strToBool(cfg.shadowquic_udp_over_stream || cfg.tuic_udp_over_stream),
"heartbeat-interval": strToInt(cfg.tuic_heartbeat) || null, "heartbeat-interval": strToInt(cfg.tuic_heartbeat) || null,
"keep-alive-interval": strToInt(cfg.shadowquic_heartbeat) || null, "keep-alive-interval": strToInt(cfg.shadowquic_heartbeat) || null,
"congestion-controller": cfg.congestion_controller, "congestion-controller": cfg.type in ['zerotier'] ? null : cfg.congestion_controller,
"bbr-profile": cfg.bbr_profile, "bbr-profile": cfg.bbr_profile,
"max-open-streams": strToInt(cfg.max_open_streams) || null, "max-open-streams": strToInt(cfg.max_open_streams) || null,
@@ -37,7 +37,7 @@ function wGET(url, header, filepath) {
return exitcode; return exitcode;
} }
const RES_TYPE = ['certs', 'provider', 'ruleset', 'resources', 'templates']; const RES_TYPE = ['certs', 'node', 'provider', 'ruleset', 'resources', 'templates'];
const methods = { const methods = {
mihomo_generator: { mihomo_generator: {
@@ -330,7 +330,7 @@ const methods = {
}, },
file_read: { file_read: {
args: { type: 'type', filename: 'filename' }, args: { type: 'type', filename: 'filename', isbinary: 'isbinary' },
call: function(req) { call: function(req) {
if (!(req.args?.type in RES_TYPE)) if (!(req.args?.type in RES_TYPE))
return { content: null, error: 'illegal type' }; return { content: null, error: 'illegal type' };
@@ -338,13 +338,17 @@ const methods = {
if ((!req.args?.filename) || match(req.args?.filename, /\.\.\//)) if ((!req.args?.filename) || match(req.args?.filename, /\.\.\//))
return { content: null, error: 'illegal filename' }; return { content: null, error: 'illegal filename' };
const filecontent = readfile(`${HM_DIR}/${req.args?.type}/${req.args?.filename}`); let filecontent = readfile(`${HM_DIR}/${req.args?.type}/${req.args?.filename}`);
if (req.args?.isbinary == 1)
filecontent = b64enc(filecontent);
return { content: filecontent }; return { content: filecontent };
} }
}, },
file_write: { file_write: {
args: { type: 'type', filename: 'filename', content: 'content' }, args: { type: 'type', filename: 'filename', content: 'content', isbinary: 'isbinary' },
call: function(req) { call: function(req) {
if (!(req.args?.type in RES_TYPE)) if (!(req.args?.type in RES_TYPE))
return { result: false, error: 'illegal type' }; return { result: false, error: 'illegal type' };
@@ -363,6 +367,9 @@ const methods = {
content += '\n'; content += '\n';
} }
if (req.args?.isbinary == 1)
content = b64dec(content);
//system(`mkdir -p ${HM_DIR}/${req.args?.type}`); //system(`mkdir -p ${HM_DIR}/${req.args?.type}`);
writefile(file, content); writefile(file, content);
@@ -403,7 +410,7 @@ const methods = {
if ((!req.args?.filename) || match(req.args?.filename, /\.\.\//)) if ((!req.args?.filename) || match(req.args?.filename, /\.\.\//))
return { result: false, error: 'illegal filename' }; return { result: false, error: 'illegal filename' };
system(`rm -f ${HM_DIR}/${req.args?.type}/${shellQuote(req.args?.filename)}`); system(`rm -rf ${HM_DIR}/${req.args?.type}/${shellQuote(req.args?.filename)}`);
return { result: true }; return { result: true };
} }
+2 -2
View File
@@ -2,8 +2,8 @@
include $(TOPDIR)/rules.mk include $(TOPDIR)/rules.mk
PKG_VERSION:=1.0.2-r20240822 PKG_VERSION:=1.0.2-r20260804
PKG_RELEASE:=4 PKG_RELEASE:=5
LUCI_TITLE:=LuCI support for FeiShuVpn LUCI_TITLE:=LuCI support for FeiShuVpn
LUCI_PKGARCH:=all LUCI_PKGARCH:=all
@@ -35,7 +35,7 @@ o = s:option(Value, "port", translate("Port").."<b>*</b>")
o.default = "9091" o.default = "9091"
o.datatype = "port" o.datatype = "port"
o = s:option(Value, "image_name", translate("Image").."<b>*</b>") o = s:option(Value, "image_name", translate("Docker Image").."<b>*</b>")
o.rmempty = false o.rmempty = false
o.datatype = "string" o.datatype = "string"
o.default = "registry.cn-qingdao.aliyuncs.com/feishuwg/p2p:latest" o.default = "registry.cn-qingdao.aliyuncs.com/feishuwg/p2p:latest"
+3
View File
@@ -13,6 +13,9 @@ msgstr "飞鼠组网是一个点对点的组网工具。"
msgid "Config path" msgid "Config path"
msgstr "配置文件路径" msgstr "配置文件路径"
msgid "Docker Image"
msgstr "镜像"
msgid "Port" msgid "Port"
msgstr "端口" msgstr "端口"
+2 -2
View File
@@ -2,8 +2,8 @@
include $(TOPDIR)/rules.mk include $(TOPDIR)/rules.mk
PKG_VERSION:=1.0.3-r20240822 PKG_VERSION:=1.0.3-r20260804
PKG_RELEASE:=4 PKG_RELEASE:=5
LUCI_TITLE:=LuCI support for Gogs LUCI_TITLE:=LuCI support for Gogs
LUCI_PKGARCH:=all LUCI_PKGARCH:=all
+1 -1
View File
@@ -28,7 +28,7 @@ o.default = "3022"
o.datatype = "string" o.datatype = "string"
o.rmempty = false o.rmempty = false
o = s:option(Value, "image_name", translate("Image").."<b>*</b>") o = s:option(Value, "image_name", translate("Docker Image").."<b>*</b>")
o.rmempty = false o.rmempty = false
o.datatype = "string" o.datatype = "string"
o:value("gogs/gogs:latest", "gogs/gogs:latest") o:value("gogs/gogs:latest", "gogs/gogs:latest")
+3
View File
@@ -10,6 +10,9 @@ msgstr "Gogs 是一个轻松易用的 Git 服务。"
msgid "Config path" msgid "Config path"
msgstr "配置文件路径" msgstr "配置文件路径"
msgid "Docker Image"
msgstr "镜像"
msgid "HTTP Port" msgid "HTTP Port"
msgstr "HTTP 端口" msgstr "HTTP 端口"
+2 -2
View File
@@ -2,8 +2,8 @@
include $(TOPDIR)/rules.mk include $(TOPDIR)/rules.mk
PKG_VERSION:=1.1.4-r20250321 PKG_VERSION:=1.1.4-r20260804
PKG_RELEASE:=4 PKG_RELEASE:=5
LUCI_TITLE:=LuCI support for homeassistant LUCI_TITLE:=LuCI support for homeassistant
LUCI_PKGARCH:=all LUCI_PKGARCH:=all
@@ -18,7 +18,7 @@ s = m:section(TypedSection, "homeassistant", translate("Setup"), translate("The
s.addremove=false s.addremove=false
s.anonymous=true s.anonymous=true
o = s:option(Value, "image_name", translate("Image").."<b>*</b>") o = s:option(Value, "image_name", translate("Docker Image").."<b>*</b>")
o.rmempty = false o.rmempty = false
o.datatype = "string" o.datatype = "string"
o:value("homeassistant/home-assistant:latest", "homeassistant/home-assistant:latest") o:value("homeassistant/home-assistant:latest", "homeassistant/home-assistant:latest")
@@ -10,6 +10,9 @@ msgstr "将本地控制和隐私放在首位的开源家庭自动化,由全世
msgid "Config path" msgid "Config path"
msgstr "配置文件路径" msgstr "配置文件路径"
msgid "Docker Image"
msgstr "镜像"
msgid "Service Status" msgid "Service Status"
msgstr "服务状态" msgstr "服务状态"
+2 -2
View File
@@ -2,8 +2,8 @@
include $(TOPDIR)/rules.mk include $(TOPDIR)/rules.mk
PKG_VERSION:=1.0.2-r20240822 PKG_VERSION:=1.0.2-r20260804
PKG_RELEASE:=4 PKG_RELEASE:=5
LUCI_TITLE:=LuCI support for HTReader LUCI_TITLE:=LuCI support for HTReader
LUCI_PKGARCH:=all LUCI_PKGARCH:=all
@@ -47,7 +47,7 @@ o = s:option(Value, "active_code", translate("Active code"))
o.datatype = "string" o.datatype = "string"
o:depends("multiuser", 1) o:depends("multiuser", 1)
o = s:option(Value, "image_name", translate("Image").."<b>*</b>") o = s:option(Value, "image_name", translate("Docker Image").."<b>*</b>")
o.rmempty = false o.rmempty = false
o.datatype = "string" o.datatype = "string"
o.default = "hectorqin/reader" o.default = "hectorqin/reader"
+3
View File
@@ -10,6 +10,9 @@ msgstr "HTReader 是一个网页版本在线读书。"
msgid "Config path" msgid "Config path"
msgstr "配置文件路径" msgstr "配置文件路径"
msgid "Docker Image"
msgstr "镜像"
msgid "Port" msgid "Port"
msgstr "端口" msgstr "端口"
+2 -2
View File
@@ -2,8 +2,8 @@
include $(TOPDIR)/rules.mk include $(TOPDIR)/rules.mk
PKG_VERSION:=1.0.2-r20250321 PKG_VERSION:=1.0.2-r20260804
PKG_RELEASE:=4 PKG_RELEASE:=5
LUCI_TITLE:=LuCI support for Immich LUCI_TITLE:=LuCI support for Immich
LUCI_PKGARCH:=all LUCI_PKGARCH:=all
+1 -1
View File
@@ -35,7 +35,7 @@ o = s:option(Value, "port", translate("Port").."<b>*</b>")
o.default = "2283" o.default = "2283"
o.datatype = "port" o.datatype = "port"
o = s:option(Value, "image_ver", translate("Image").."<b>*</b>") o = s:option(Value, "image_ver", translate("Docker Image").."<b>*</b>")
o.rmempty = false o.rmempty = false
o.datatype = "string" o.datatype = "string"
o.default = "release" o.default = "release"
+3
View File
@@ -13,6 +13,9 @@ msgstr "Immich 是私有化的相册视频管理工具。很占资源,网络
msgid "Config path" msgid "Config path"
msgstr "配置文件路径" msgstr "配置文件路径"
msgid "Docker Image"
msgstr "镜像"
msgid "Port" msgid "Port"
msgstr "端口" msgstr "端口"
+2 -2
View File
@@ -2,8 +2,8 @@
include $(TOPDIR)/rules.mk include $(TOPDIR)/rules.mk
PKG_VERSION:=1.1.7-r20241211 PKG_VERSION:=1.1.7-r20260804
PKG_RELEASE:=4 PKG_RELEASE:=5
LUCI_TITLE:=LuCI support for istoredup LUCI_TITLE:=LuCI support for istoredup
LUCI_PKGARCH:=all LUCI_PKGARCH:=all
@@ -31,7 +31,7 @@ s = m:section(TypedSection, "istoredup", translate("Setup"),
s.addremove=false s.addremove=false
s.anonymous=true s.anonymous=true
o = s:option(Value, "image_name", translate("Image").."<b>*</b>") o = s:option(Value, "image_name", translate("Docker Image").."<b>*</b>")
o.rmempty = false o.rmempty = false
o.datatype = "string" o.datatype = "string"
o:value("linkease/istoreduprk35xx:latest", "linkease/istoreduprk35xx:latest") o:value("linkease/istoreduprk35xx:latest", "linkease/istoreduprk35xx:latest")
+3
View File
@@ -7,6 +7,9 @@ msgstr "官方网站:"
msgid "A duplica of iStoreOS." msgid "A duplica of iStoreOS."
msgstr "iStoreOS 的分身,可以把风险高的软件安装到分身里面,出问题不影响到主系统。" msgstr "iStoreOS 的分身,可以把风险高的软件安装到分身里面,出问题不影响到主系统。"
msgid "Docker Image"
msgstr "镜像"
msgid "Service Status" msgid "Service Status"
msgstr "服务状态" msgstr "服务状态"
+2 -2
View File
@@ -2,8 +2,8 @@
include $(TOPDIR)/rules.mk include $(TOPDIR)/rules.mk
PKG_VERSION:=1.0.6-r20250406 PKG_VERSION:=1.0.6-r20260804
PKG_RELEASE:=4 PKG_RELEASE:=5
LUCI_TITLE:=LuCI support for 1Panel LUCI_TITLE:=LuCI support for 1Panel
LUCI_PKGARCH:=all LUCI_PKGARCH:=all
@@ -52,7 +52,7 @@ o:value("v1.10.11-lts", "v1.10.11-lts")
o:value("v1.10.28-lts", "v1.10.28-lts") o:value("v1.10.28-lts", "v1.10.28-lts")
o.default = 'v1.10.28-lts' o.default = 'v1.10.28-lts'
o = s:option(Value, "image_name", translate("Image").."<b>*</b>") o = s:option(Value, "image_name", translate("Docker Image").."<b>*</b>")
o.rmempty = false o.rmempty = false
o.datatype = "string" o.datatype = "string"
o:value("linkease/istorepanel:latest", "linkease/istorepanel:latest") o:value("linkease/istorepanel:latest", "linkease/istorepanel:latest")
@@ -10,6 +10,9 @@ msgstr "1Panel 是新一代的 Linux 服务器运维管理面板"
msgid "Config path" msgid "Config path"
msgstr "配置文件路径" msgstr "配置文件路径"
msgid "Docker Image"
msgstr "镜像"
msgid "Port" msgid "Port"
msgstr "端口" msgstr "端口"
+2 -2
View File
@@ -2,8 +2,8 @@
include $(TOPDIR)/rules.mk include $(TOPDIR)/rules.mk
PKG_VERSION:=1.0.4-r20250321 PKG_VERSION:=1.0.4-r20260804
PKG_RELEASE:=4 PKG_RELEASE:=5
LUCI_TITLE:=LuCI support for ITTools LUCI_TITLE:=LuCI support for ITTools
LUCI_PKGARCH:=all LUCI_PKGARCH:=all
@@ -35,7 +35,7 @@ o.default = "9070"
o.datatype = "port" o.datatype = "port"
o:depends("hostnet", 0) o:depends("hostnet", 0)
o = s:option(Value, "image_name", translate("Image").."<b>*</b>") o = s:option(Value, "image_name", translate("Docker Image").."<b>*</b>")
o.rmempty = false o.rmempty = false
o.datatype = "string" o.datatype = "string"
o.default = "corentinth/it-tools:latest" o.default = "corentinth/it-tools:latest"
+3
View File
@@ -1,6 +1,9 @@
msgid "" msgid ""
msgstr "Content-Type: text/plain; charset=UTF-8" msgstr "Content-Type: text/plain; charset=UTF-8"
msgid "Docker Image"
msgstr "镜像"
msgid "ITTools" msgid "ITTools"
msgid "开发工具集" msgid "开发工具集"
+2 -2
View File
@@ -2,8 +2,8 @@
include $(TOPDIR)/rules.mk include $(TOPDIR)/rules.mk
PKG_VERSION:=1.1.1-r20240822 PKG_VERSION:=1.1.1-r20260804
PKG_RELEASE:=4 PKG_RELEASE:=5
LUCI_TITLE:=LuCI support for jackett LUCI_TITLE:=LuCI support for jackett
LUCI_PKGARCH:=all LUCI_PKGARCH:=all
@@ -23,7 +23,7 @@ o.rmempty = false
o.default = "9117" o.default = "9117"
o.datatype = "port" o.datatype = "port"
o = s:option(Value, "image_name", translate("Image").."<b>*</b>") o = s:option(Value, "image_name", translate("Docker Image").."<b>*</b>")
o.rmempty = false o.rmempty = false
o.datatype = "string" o.datatype = "string"
o:value("linuxserver/jackett:latest", "linuxserver/jackett:latest") o:value("linuxserver/jackett:latest", "linuxserver/jackett:latest")
+3
View File
@@ -10,6 +10,9 @@ msgstr "Jackett 是维护索引器抓取和翻译逻辑的单一存储库 - 消
msgid "Config path" msgid "Config path"
msgstr "配置文件路径" msgstr "配置文件路径"
msgid "Docker Image"
msgstr "镜像"
msgid "Torrent save path" msgid "Torrent save path"
msgstr "种子保存路径" msgstr "种子保存路径"
+2 -2
View File
@@ -2,8 +2,8 @@
include $(TOPDIR)/rules.mk include $(TOPDIR)/rules.mk
PKG_VERSION:=1.2.2-r20250321 PKG_VERSION:=1.2.2-r20260804
PKG_RELEASE:=4 PKG_RELEASE:=5
LUCI_TITLE:=LuCI support for jellyfin LUCI_TITLE:=LuCI support for jellyfin
LUCI_PKGARCH:=all LUCI_PKGARCH:=all
@@ -27,7 +27,7 @@ s = m:section(TypedSection, "jellyfin", translate("Setup"),
s.addremove=false s.addremove=false
s.anonymous=true s.anonymous=true
o = s:option(Value, "image", translate("Image")) o = s:option(Value, "image", translate("Docker Image"))
o.datatype = "string" o.datatype = "string"
o:value("", translate("Default")) o:value("", translate("Default"))
if luci.sys.call("grep -q 'rockchip,' /proc/device-tree/compatible 2>/dev/null") == 0 then if luci.sys.call("grep -q 'rockchip,' /proc/device-tree/compatible 2>/dev/null") == 0 then
+1 -1
View File
@@ -22,7 +22,7 @@ msgstr "x86 系列"
msgid "Raspberry Pi series" msgid "Raspberry Pi series"
msgstr "树莓派系列" msgstr "树莓派系列"
msgid "Image" msgid "Docker Image"
msgstr "镜像" msgstr "镜像"
msgid "Default" msgid "Default"
+1 -1
View File
@@ -9,7 +9,7 @@ LUCI_TITLE:=LuCI support for KodExplorer
LUCI_DEPENDS:=+nginx-ssl +unzip +zoneinfo-asia +php8 +php8-fastcgi +php8-fpm +php8-mod-curl +php8-mod-dom +php8-mod-gd +php8-mod-iconv +php8-mod-mbstring +php8-mod-opcache +php8-mod-session +php8-mod-zip +php8-mod-sqlite3 +php8-mod-pdo +php8-mod-pdo-sqlite +php8-mod-pdo-mysql +php8-mod-xml +php8-mod-xmlreader +php8-mod-xmlwriter LUCI_DEPENDS:=+nginx-ssl +unzip +zoneinfo-asia +php8 +php8-fastcgi +php8-fpm +php8-mod-curl +php8-mod-dom +php8-mod-gd +php8-mod-iconv +php8-mod-mbstring +php8-mod-opcache +php8-mod-session +php8-mod-zip +php8-mod-sqlite3 +php8-mod-pdo +php8-mod-pdo-sqlite +php8-mod-pdo-mysql +php8-mod-xml +php8-mod-xmlreader +php8-mod-xmlwriter
LUCI_PKGARCH:=all LUCI_PKGARCH:=all
PKG_VERSION:=20220109 PKG_VERSION:=20220109
PKG_RELEASE:=2 PKG_RELEASE:=3
include $(TOPDIR)/feeds/luci/luci.mk include $(TOPDIR)/feeds/luci/luci.mk
@@ -1,19 +1,4 @@
#!/bin/sh #!/bin/sh
[ ! -f "/usr/share/ucitrack/luci-app-kodexplorer.json" ] && {
cat > /usr/share/ucitrack/luci-app-kodexplorer.json << EEOF
{
"config": "kodexplorer",
"init": "kodexplorer"
}
EEOF
}
uci -q batch <<-EOF >/dev/null
delete ucitrack.@kodexplorer[-1]
add ucitrack kodexplorer
set ucitrack.@kodexplorer[-1].init=kodexplorer
commit ucitrack
EOF
/etc/init.d/php8-fpm stop /etc/init.d/php8-fpm stop
/etc/init.d/php8-fpm disable /etc/init.d/php8-fpm disable
+2 -2
View File
@@ -2,8 +2,8 @@
include $(TOPDIR)/rules.mk include $(TOPDIR)/rules.mk
PKG_VERSION:=1.0.1-r20240822 PKG_VERSION:=1.0.1-r20260804
PKG_RELEASE:=4 PKG_RELEASE:=5
LUCI_TITLE:=LuCI support for LANraragi LUCI_TITLE:=LuCI support for LANraragi
LUCI_PKGARCH:=all LUCI_PKGARCH:=all
@@ -24,7 +24,7 @@ o.rmempty = false
o.default = "3000" o.default = "3000"
o.datatype = "string" o.datatype = "string"
o = s:option(Value, "image_name", translate("Image").."<b>*</b>") o = s:option(Value, "image_name", translate("Docker Image").."<b>*</b>")
o.rmempty = false o.rmempty = false
o.datatype = "string" o.datatype = "string"
o:value("dezhao/lanraragi_cn", "dezhao/lanraragi_cn") o:value("dezhao/lanraragi_cn", "dezhao/lanraragi_cn")
+3
View File
@@ -13,6 +13,9 @@ msgstr "配置文件路径"
msgid "Content path" msgid "Content path"
msgstr "资源路径" msgstr "资源路径"
msgid "Docker Image"
msgstr "镜像"
msgid "HTTP Port" msgid "HTTP Port"
msgstr "HTTP 端口" msgstr "HTTP 端口"
+2 -2
View File
@@ -2,8 +2,8 @@
include $(TOPDIR)/rules.mk include $(TOPDIR)/rules.mk
PKG_VERSION:=1.0.2-r20240822 PKG_VERSION:=1.0.2-r20260804
PKG_RELEASE:=4 PKG_RELEASE:=5
LUCI_TITLE:=LuCI support for Memos LUCI_TITLE:=LuCI support for Memos
LUCI_PKGARCH:=all LUCI_PKGARCH:=all
+1 -1
View File
@@ -22,7 +22,7 @@ o = s:option(Value, "http_port", translate("HTTP Port").."<b>*</b>")
o.default = "5230" o.default = "5230"
o.datatype = "port" o.datatype = "port"
o = s:option(Value, "image_name", translate("Image").."<b>*</b>") o = s:option(Value, "image_name", translate("Docker Image").."<b>*</b>")
o.rmempty = false o.rmempty = false
o.datatype = "string" o.datatype = "string"
o:value("neosmemo/memos:latest", "neosmemo/memos:latest") o:value("neosmemo/memos:latest", "neosmemo/memos:latest")
+3
View File
@@ -13,6 +13,9 @@ msgstr "Memos 是一个开源的极简的碎片化知识管理工具。"
msgid "Config path" msgid "Config path"
msgstr "配置文件路径" msgstr "配置文件路径"
msgid "Docker Image"
msgstr "镜像"
msgid "HTTP Port" msgid "HTTP Port"
msgstr "HTTP 端口" msgstr "HTTP 端口"
+2 -2
View File
@@ -2,8 +2,8 @@
include $(TOPDIR)/rules.mk include $(TOPDIR)/rules.mk
PKG_VERSION:=1.0.2-r20240822 PKG_VERSION:=1.0.2-r20260804
PKG_RELEASE:=4 PKG_RELEASE:=5
LUCI_TITLE:=LuCI support for MTPhotos LUCI_TITLE:=LuCI support for MTPhotos
LUCI_PKGARCH:=all LUCI_PKGARCH:=all
@@ -36,7 +36,7 @@ o.default = "8063"
o.datatype = "port" o.datatype = "port"
o:depends("hostnet", 0) o:depends("hostnet", 0)
o = s:option(Value, "image_name", translate("Image").."<b>*</b>") o = s:option(Value, "image_name", translate("Docker Image").."<b>*</b>")
o.rmempty = false o.rmempty = false
o.datatype = "string" o.datatype = "string"
o:value("mtphotos/mt-photos:nodb-latest", "mtphotos/mt-photos:nodb-latest") o:value("mtphotos/mt-photos:nodb-latest", "mtphotos/mt-photos:nodb-latest")
+3
View File
@@ -10,6 +10,9 @@ msgstr "MTPhotos 是一个相册管理软件。"
msgid "Config path" msgid "Config path"
msgstr "配置文件路径" msgstr "配置文件路径"
msgid "Docker Image"
msgstr "镜像"
msgid "Upload path" msgid "Upload path"
msgstr "上传文件路径" msgstr "上传文件路径"
+2 -2
View File
@@ -2,8 +2,8 @@
include $(TOPDIR)/rules.mk include $(TOPDIR)/rules.mk
PKG_VERSION:=1.1.3-r20240822 PKG_VERSION:=1.1.3-r20260804
PKG_RELEASE:=4 PKG_RELEASE:=5
LUCI_TITLE:=LuCI support for nastools LUCI_TITLE:=LuCI support for nastools
LUCI_PKGARCH:=all LUCI_PKGARCH:=all
@@ -23,7 +23,7 @@ o.rmempty = false
o.default = "3003" o.default = "3003"
o.datatype = "port" o.datatype = "port"
o = s:option(Value, "image_name", translate("Image").."<b>*</b>") o = s:option(Value, "image_name", translate("Docker Image").."<b>*</b>")
o.rmempty = false o.rmempty = false
o.datatype = "string" o.datatype = "string"
o:value("jxxghp/nas-tools", "jxxghp/nas-tools") o:value("jxxghp/nas-tools", "jxxghp/nas-tools")
+3
View File
@@ -10,6 +10,9 @@ msgstr "NasTools 是汇聚了电影搜索,下载,订阅,观看等等 NAS
msgid "Config path" msgid "Config path"
msgstr "配置文件路径" msgstr "配置文件路径"
msgid "Docker Image"
msgstr "镜像"
msgid "Port" msgid "Port"
msgstr "端口" msgstr "端口"
+2 -2
View File
@@ -2,8 +2,8 @@
include $(TOPDIR)/rules.mk include $(TOPDIR)/rules.mk
PKG_VERSION:=1.0.1-r20240822 PKG_VERSION:=1.0.1-r20260804
PKG_RELEASE:=4 PKG_RELEASE:=5
LUCI_TITLE:=LuCI support for Navidrome LUCI_TITLE:=LuCI support for Navidrome
LUCI_PKGARCH:=all LUCI_PKGARCH:=all
@@ -23,7 +23,7 @@ o.rmempty = false
o.default = "3000" o.default = "3000"
o.datatype = "string" o.datatype = "string"
o = s:option(Value, "image_name", translate("Image").."<b>*</b>") o = s:option(Value, "image_name", translate("Docker Image").."<b>*</b>")
o.rmempty = false o.rmempty = false
o.datatype = "string" o.datatype = "string"
o:value("difegue/navidrome", "difegue/navidrome") o:value("difegue/navidrome", "difegue/navidrome")
+3
View File
@@ -10,6 +10,9 @@ msgstr "Navidrome 是一个开源的在线音乐播放软件。"
msgid "Config path" msgid "Config path"
msgstr "配置文件路径" msgstr "配置文件路径"
msgid "Docker Image"
msgstr "镜像"
msgid "Music path" msgid "Music path"
msgstr "音乐路径" msgstr "音乐路径"
+2 -2
View File
@@ -2,8 +2,8 @@
include $(TOPDIR)/rules.mk include $(TOPDIR)/rules.mk
PKG_VERSION:=1.1.1-r20240822 PKG_VERSION:=1.1.1-r20260804
PKG_RELEASE:=4 PKG_RELEASE:=5
LUCI_TITLE:=LuCI support for nextcloud LUCI_TITLE:=LuCI support for nextcloud
LUCI_PKGARCH:=all LUCI_PKGARCH:=all
@@ -23,7 +23,7 @@ o.rmempty = false
o.default = "8082" o.default = "8082"
o.datatype = "port" o.datatype = "port"
o = s:option(Value, "image_name", translate("Image").."<b>*</b>") o = s:option(Value, "image_name", translate("Docker Image").."<b>*</b>")
o.rmempty = false o.rmempty = false
o.datatype = "string" o.datatype = "string"
o:value("nextcloud", "nextcloud") o:value("nextcloud", "nextcloud")
+3
View File
@@ -10,6 +10,9 @@ msgstr "所有数据的安全之家。 根据您的条件,从任何设备访
msgid "Config path" msgid "Config path"
msgstr "配置文件路径" msgstr "配置文件路径"
msgid "Docker Image"
msgstr "镜像"
msgid "Port" msgid "Port"
msgstr "端口" msgstr "端口"
+1 -1
View File
@@ -1,7 +1,7 @@
include $(TOPDIR)/rules.mk include $(TOPDIR)/rules.mk
PKG_VERSION:=1.26.1 PKG_VERSION:=1.26.1
PKG_RELEASE:=7 PKG_RELEASE:=8
LUCI_TITLE:=LuCI Support for nikki LUCI_TITLE:=LuCI Support for nikki
LUCI_DEPENDS:=+luci-base +nikki LUCI_DEPENDS:=+luci-base +nikki
@@ -82,7 +82,7 @@ const methods = {
const protocol = api_tls_listen ? 'https' : 'http'; const protocol = api_tls_listen ? 'https' : 'http';
const url = api_listen + path; const url = (api_tls_listen ?? api_listen) + path;
const process = popen(`curl --proto-default '${protocol}' --insecure --request '${method}' --oauth2-bearer '${api_secret}' --url-query '${query}' --data '${body}' '${url}'`); const process = popen(`curl --proto-default '${protocol}' --insecure --request '${method}' --oauth2-bearer '${api_secret}' --url-query '${query}' --data '${body}' '${url}'`);
if (process) { if (process) {
+2 -2
View File
@@ -2,8 +2,8 @@
include $(TOPDIR)/rules.mk include $(TOPDIR)/rules.mk
PKG_VERSION:=1.0.1-r20250403 PKG_VERSION:=1.0.1-r20260804
PKG_RELEASE:=4 PKG_RELEASE:=5
LUCI_TITLE:=LuCI support for OneAPI LUCI_TITLE:=LuCI support for OneAPI
LUCI_PKGARCH:=all LUCI_PKGARCH:=all
+1 -1
View File
@@ -36,7 +36,7 @@ o.default = "3005"
o.datatype = "port" o.datatype = "port"
o:depends("hostnet", 0) o:depends("hostnet", 0)
o = s:option(Value, "image_name", translate("Image").."<b>*</b>") o = s:option(Value, "image_name", translate("Docker Image").."<b>*</b>")
o.rmempty = false o.rmempty = false
o.datatype = "string" o.datatype = "string"
o:value("justsong/one-api:latest", "justsong/one-api:latest") o:value("justsong/one-api:latest", "justsong/one-api:latest")
+3
View File
@@ -10,6 +10,9 @@ msgstr "OneAPI 是LLM API 管理 & 分发系统,支持 OpenAI、Azure、Anthro
msgid "Config path" msgid "Config path"
msgstr "配置文件路径" msgstr "配置文件路径"
msgid "Docker Image"
msgstr "镜像"
msgid "Port" msgid "Port"
msgstr "端口" msgstr "端口"
+2 -2
View File
@@ -2,8 +2,8 @@
include $(TOPDIR)/rules.mk include $(TOPDIR)/rules.mk
PKG_VERSION:=1.0.8-r20250207 PKG_VERSION:=1.0.8-r20260804
PKG_RELEASE:=4 PKG_RELEASE:=5
LUCI_TITLE:=LuCI support for OpenWebUI LUCI_TITLE:=LuCI support for OpenWebUI
LUCI_PKGARCH:=all LUCI_PKGARCH:=all
@@ -35,7 +35,7 @@ o = s:option(Value, "port", translate("Port").."<b>*</b>")
o.default = "3000" o.default = "3000"
o.datatype = "port" o.datatype = "port"
o = s:option(Value, "image_name", translate("Image").."<b>*</b>") o = s:option(Value, "image_name", translate("Docker Image").."<b>*</b>")
o.rmempty = false o.rmempty = false
o.datatype = "string" o.datatype = "string"
o:value("ghcr.io/open-webui/open-webui:main", "ghcr.io/open-webui/open-webui:main") o:value("ghcr.io/open-webui/open-webui:main", "ghcr.io/open-webui/open-webui:main")
+3
View File
@@ -10,6 +10,9 @@ msgstr "Open WebUI 是一款可扩展、自托管的 AI 界面,能够无缝适
msgid "Config path" msgid "Config path"
msgstr "配置文件路径" msgstr "配置文件路径"
msgid "Docker Image"
msgstr "镜像"
msgid "Port" msgid "Port"
msgstr "端口" msgstr "端口"
+2 -2
View File
@@ -2,8 +2,8 @@
include $(TOPDIR)/rules.mk include $(TOPDIR)/rules.mk
PKG_VERSION:=1.0.2-r20240822 PKG_VERSION:=1.0.2-r20260804
PKG_RELEASE:=4 PKG_RELEASE:=5
LUCI_TITLE:=LuCI support for Owntone LUCI_TITLE:=LuCI support for Owntone
LUCI_PKGARCH:=all LUCI_PKGARCH:=all
@@ -18,7 +18,7 @@ s = m:section(TypedSection, "main", translate("Setup"), translate("The following
s.addremove=false s.addremove=false
s.anonymous=true s.anonymous=true
o = s:option(Value, "image_name", translate("Image").."<b>*</b>", translate("Owntone only works in host network with port 3689")) o = s:option(Value, "image_name", translate("Docker Image").."<b>*</b>", translate("Owntone only works in host network with port 3689"))
o.rmempty = false o.rmempty = false
o.datatype = "string" o.datatype = "string"
o:value("lscr.io/linuxserver/daapd:latest", "lscr.io/linuxserver/daapd:latest") o:value("lscr.io/linuxserver/daapd:latest", "lscr.io/linuxserver/daapd:latest")
Loaded 100 of 144 files, more files were not shown because too many files have changed in this diff. Show more