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

This commit is contained in:
github-actions[bot]
2026-08-04 20:40:02 +08:00
parent d56fd78efc
commit 9b522ebf42
144 changed files with 4375 additions and 3375 deletions
+1 -1
View File
@@ -7,7 +7,7 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=airconnect
PKG_VERSION:=1.10.1
PKG_VERSION:=1.11.1
PKG_RELEASE=1
PKG_SOURCE:=AirConnect-$(PKG_VERSION).zip
+1 -1
View File
@@ -6,7 +6,7 @@ include $(TOPDIR)/rules.mk
PKG_NAME:=daed
PKG_VERSION:=2026.08.03
PKG_RELEASE:=40
PKG_RELEASE:=41
PKG_SOURCE:=daed-src-2026.08.03-5f7939adc12a.tar.gz
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
PKG_VERSION:=1.0.0-r20250610
PKG_RELEASE:=4
PKG_VERSION:=1.0.0-r20260804
PKG_RELEASE:=5
LUCI_TITLE:=LuCI support for arcadia
LUCI_PKGARCH:=all
@@ -24,7 +24,7 @@ s = m:section(TypedSection, "arcadia", translate("Setup"),
s.addremove=false
s.anonymous=true
o = s:option(Value, "image", translate("Image"))
o = s:option(Value, "image", translate("Docker Image"))
o.datatype = "string"
o:value("", translate("Default"))
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."
msgstr "初始的用户名和密码分别是'useradmin'和'passwd'。"
msgid "Image"
msgid "Docker Image"
msgstr "镜像"
msgid "Default"
+2 -2
View File
@@ -2,8 +2,8 @@
include $(TOPDIR)/rules.mk
PKG_VERSION:=1.0.3-r20240822
PKG_RELEASE:=4
PKG_VERSION:=1.0.3-r20260804
PKG_RELEASE:=5
LUCI_TITLE:=LuCI support for ChineseSubFinder
LUCI_PKGARCH:=all
@@ -28,7 +28,7 @@ o.rmempty = false
o.default = "19037"
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.datatype = "string"
o:value("allanpk716/chinesesubfinder:latest-lite", "allanpk716/chinesesubfinder:latest-lite")
@@ -10,6 +10,9 @@ msgstr "ChineseSubFinder是一个自动化中文字幕下载工具。"
msgid "Config path"
msgstr "配置文件路径"
msgid "Docker Image"
msgstr "镜像"
msgid "HTTP Port"
msgstr "HTTP 端口"
+2 -2
View File
@@ -2,8 +2,8 @@
include $(TOPDIR)/rules.mk
PKG_VERSION:=1.0.2-r20251011
PKG_RELEASE:=4
PKG_VERSION:=1.0.2-r20260804
PKG_RELEASE:=5
LUCI_TITLE:=LuCI support for CodeServer
LUCI_PKGARCH:=all
@@ -22,7 +22,7 @@ o = s:option(Value, "http_port", translate("HTTP Port").."<b>*</b>")
o.default = "8085"
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.datatype = "string"
o:value("lscr.io/linuxserver/code-server:latest", "lscr.io/linuxserver/code-server:latest")
@@ -10,6 +10,9 @@ msgstr "CodeServer 就一个在线版本的 VSCode,可以在线开发。"
msgid "Config path"
msgstr "配置文件路径"
msgid "Docker Image"
msgstr "镜像"
msgid "HTTP Port"
msgstr "HTTP 端口"
+1 -1
View File
@@ -9,7 +9,7 @@ LUCI_TITLE:=LuCI support for Timewol
LUCI_DEPENDS:=+etherwake
LUCI_PKGARCH:=all
PKG_VERSION:=1.1
PKG_RELEASE:=2
PKG_RELEASE:=3
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_PKGARCH:=all
PKG_VERSION:=1.1
PKG_RELEASE:=2
PKG_RELEASE:=3
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_PKGARCH:=all
PKG_VERSION:=1.1
PKG_RELEASE:=2
PKG_RELEASE:=3
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
PKG_VERSION:=1.2.0-r20250625
PKG_RELEASE:=4
PKG_VERSION:=1.2.0-r20260804
PKG_RELEASE:=5
LUCI_TITLE:=LuCI support for demon
LUCI_PKGARCH:=all
+1 -1
View File
@@ -21,7 +21,7 @@ o = s:option(Value, "port", translate("Port").."<b>*</b>")
o.default = "18888"
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.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")
+3
View File
@@ -7,6 +7,9 @@ msgstr "容器魔王"
msgid "Cache path"
msgstr "缓存文件路径"
msgid "Docker Image"
msgstr "镜像"
msgid "Service Status"
msgstr "服务状态"
+2 -3
View File
@@ -2,9 +2,8 @@
include $(TOPDIR)/rules.mk
# Updated: 2025-10-09
PKG_VERSION:=1.0.0-r20251009
PKG_RELEASE:=4
PKG_VERSION:=1.0.0-r20260804
PKG_RELEASE:=5
LUCI_TITLE:=LuCI support for DPanel
LUCI_PKGARCH:=all
+1 -1
View File
@@ -35,7 +35,7 @@ o = s:option(Value, "port", translate("Port").."<b>*</b>")
o.default = "8807"
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.datatype = "string"
o:value("dpanel/dpanel:lite", "dpanel/dpanel:lite")
+1 -1
View File
@@ -10,7 +10,7 @@ msgstr "DPanel — 轻量级仪表盘服务。"
msgid "DPanel"
msgstr "DPanel"
msgid "Image"
msgid "Docker Image"
msgstr "镜像"
msgid "Config path"
+2 -2
View File
@@ -2,8 +2,8 @@
include $(TOPDIR)/rules.mk
PKG_VERSION:=1.0.3-r20240822
PKG_RELEASE:=4
PKG_VERSION:=1.0.3-r20260804
PKG_RELEASE:=5
LUCI_TITLE:=LuCI support for Emby
LUCI_PKGARCH:=all
+1 -1
View File
@@ -27,7 +27,7 @@ o.default = "8097"
o.datatype = "port"
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.datatype = "string"
o:value("emby/embyserver", "emby/embyserver")
+3
View File
@@ -10,6 +10,9 @@ msgstr "Emby 是一个多媒体串流平台。"
msgid "Config path"
msgstr "配置文件路径"
msgid "Docker Image"
msgstr "镜像"
msgid "HTTP Port"
msgstr "HTTP 端口"
+2 -2
View File
@@ -2,8 +2,8 @@
include $(TOPDIR)/rules.mk
PKG_VERSION:=1.0.0-r20240822
PKG_RELEASE:=4
PKG_VERSION:=1.0.0-r20260804
PKG_RELEASE:=5
LUCI_TITLE:=LuCI support for Excalidraw
LUCI_PKGARCH:=all
@@ -35,7 +35,7 @@ o = s:option(Value, "port", translate("Port").."<b>*</b>")
o.default = "8090"
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.datatype = "string"
o.default = "v0.13.0"
@@ -13,6 +13,9 @@ msgstr "Excalidraw 是支持多人协同的私有化在线画板工具。"
msgid "Config path"
msgstr "配置文件路径"
msgid "Docker Image"
msgstr "镜像"
msgid "Port"
msgstr "端口"
+1
View File
@@ -20,6 +20,7 @@ PKG_NAME:=luci-app-fchomo
define Package/luci-app-fchomo/conffiles
/etc/config/fchomo
/etc/fchomo/certs/
/etc/fchomo/node/
/etc/fchomo/provider/
/etc/fchomo/ruleset/
/etc/fchomo/resources/
@@ -204,7 +204,9 @@ const outbound_type = [
['hysteria2', _('Hysteria2') + ' - ' + _('UDP')],
['shadowquic', _('ShadowQUIC') + ' - ' + _('UDP')],
['trusttunnel', _('TrustTunnel') + ' - ' + _('TCP/UDP')],
['zerotier', _('ZeroTier') + ' - ' + _('UDP') + ' - ' + _('L2')], // Endpoint
['wireguard', _('WireGuard') + ' - ' + _('UDP')], // Endpoint
['tailscale', _('Tailscale') + ' - ' + _('UDP')], // Endpoint
['masque', _('Masque') + ' - ' + _('UDP')], // Endpoint // https://blog.cloudflare.com/post-quantum-warp/
['ssh', _('SSH') + ' - ' + _('TCP')]
];
@@ -1879,6 +1881,18 @@ function validateUrl(section_id, value) {
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) {
/* Thanks to luci-proto-wireguard */
if (value)
@@ -1986,15 +2000,15 @@ function lsDir(type) {
});
}
function readFile(type, filename) {
function readFile(type, filename, isbinary) {
const callReadFile = rpc.declare({
object: 'luci.fchomo',
method: 'file_read',
params: ['type', 'filename'],
params: ['type', 'filename', 'isbinary'],
expect: { '': {} }
});
return L.resolveDefault(callReadFile(type, filename), {}).then((res) => {
return L.resolveDefault(callReadFile(type, filename, isbinary), {}).then((res) => {
if (res.content ?? true) {
return res.content;
} else
@@ -2002,15 +2016,15 @@ function readFile(type, filename) {
});
}
function writeFile(type, filename, content) {
function writeFile(type, filename, content, isbinary) {
const callWriteFile = rpc.declare({
object: 'luci.fchomo',
method: 'file_write',
params: ['type', 'filename', 'content'],
params: ['type', 'filename', 'content', 'isbinary'],
expect: { '': {} }
});
return L.resolveDefault(callWriteFile(type, filename, content), {}).then((res) => {
return L.resolveDefault(callWriteFile(type, filename, content, isbinary), {}).then((res) => {
if (res.result) {
return res.result;
} else
@@ -2182,6 +2196,7 @@ return baseclass.extend({
validateUUID,
validateUrl,
// validate with bind this
validateHexstr,
validateBase64Key,
validateMTLSClientAuth,
validatePresetIDs,
@@ -232,6 +232,19 @@ return view.extend({
ss.hm_prefmt = hm.glossary[ss.sectiontype].prefmt;
ss.hm_field = hm.glossary[ss.sectiontype].field;
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_plugin', _('Plugin fields'));
@@ -261,12 +274,12 @@ return view.extend({
so = ss.taboption('field_general', form.Value, 'server', _('Server address'));
so.datatype = 'host';
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.datatype = 'port';
so.rmempty = false;
so.depends({type: /^(rematch|direct|mieru)$/, '!reverse': true});
so.depends({type: /^(rematch|direct|mieru|zerotier|tailscale)$/, '!reverse': true});
/* Rematch fields */
// https://github.com/MetaCubeX/mihomo/pull/2862
@@ -782,15 +795,131 @@ return view.extend({
so.depends('type', 'trusttunnel');
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 */
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.'));
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.validate = L.bind(hm.validateBase64Key, so, 44);
so.rmempty = false;
so.depends('type', 'wireguard');
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'),
_('WireGuard peer public key.'));
so.validate = L.bind(hm.validateBase64Key, so, 44);
@@ -823,6 +952,45 @@ return view.extend({
so.depends('type', 'wireguard');
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 */
so = ss.taboption('field_general', form.Value, 'masque_private_key', _('Private key'),
_('Base64 encoded ECDSA private key on the NIST P-256 curve.'));
@@ -832,7 +1000,7 @@ return view.extend({
so.depends('type', 'masque');
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.'));
so.validate = L.bind(hm.validateBase64Key, so, 124);
so.rmempty = false;
@@ -887,6 +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.modalonly = true;
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.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.modalonly = true;
@@ -1124,7 +1293,7 @@ return view.extend({
so.default = so.disabled;
so.validate = function(section_id, value) {
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
if (['trojan', 'anytls', 'tuic', 'hysteria', 'hysteria2', 'shadowquic', 'trusttunnel', 'masque'].includes(type)) {
@@ -1315,6 +1484,87 @@ return view.extend({
// @VMess-TLSmirror 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'),
_('The %s address used by local machine in the %s network.').format(_('IPv4'), _('VPN')));
so.datatype = 'or(ip4addr(1), cidr4)';
@@ -1374,6 +1624,9 @@ return view.extend({
let def_mtu;
switch (type) {
case 'zerotier':
def_mtu = '1400';
break;
case 'wireguard':
def_mtu = '1408';
break;
@@ -1385,14 +1638,14 @@ return view.extend({
return true;
}
so.depends('type', 'wireguard');
so.depends({type: /^(zerotier|wireguard)$/});
so.depends({type: 'masque', masque_network: /^(|h2)$/});
so.modalonly = true;
so = ss.taboption('field_vpn', form.Flag, 'endpoint_remote_dns_resolve', _('Remote DNS resolve'),
_('Force DNS remote resolution.'));
so.default = so.disabled;
so.depends({type: /^(wireguard|masque)$/});
so.depends({type: /^(zerotier|wireguard|masque)$/});
so.modalonly = true;
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() {
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 auto_firewall "$1" "auto_firewall" "1"
config_get listen "$1" "listen" "::"
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
[ "$auto_firewall" = "0" ] && return 0
@@ -366,6 +371,7 @@ start_service() {
json_add_array firewall
# meta l4proto %s th dport %s counter accept comment "!%s: accept server instance [%s]"
config_foreach add_firewall "inbound"
config_foreach add_firewall "node"
json_close_array
procd_close_data
@@ -454,6 +460,10 @@ stop_service() {
[ -z "$1" -o "$1" = "mihomo-c" ] && stop_client
# 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
echo 2>"/dev/null" > "$RUN_DIR/fchomo_pre.nft"
return 0
@@ -1,7 +1,7 @@
#!/bin/sh
# 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
done
@@ -514,12 +514,14 @@ uci.foreach(uciconf, ucinode, (cfg) => {
"target-rematch-name": cfg.target_rematch_name,
"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,
uuid: cfg.vmess_uuid || cfg.uuid,
cipher: cfg.vmess_chipher || cfg.shadowsocks_chipher,
password: cfg.shadowsocks_password || cfg.password,
headers: cfg.headers ? json(cfg.headers) : null,
network: cfg.zerotier_network_id || cfg.masque_network || null,
"state-dir": `${HM_DIR}/${ucinode}/${cfg['.name']}`,
/* Shadowsocks */
@@ -627,14 +629,50 @@ uci.foreach(uciconf, ucinode, (cfg) => {
"health-check": cfg.type === 'trusttunnel' ? (cfg.trusttunnel_health_check === '0' ? false : true) : null,
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 */
"pre-shared-key": cfg.wireguard_pre_shared_key,
"allowed-ips": cfg.wireguard_allowed_ips,
reserved: cfg.wireguard_reserved,
"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 */
network: cfg.masque_network || null,
/* SSH */
"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),
"heartbeat-interval": strToInt(cfg.tuic_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,
"max-open-streams": strToInt(cfg.max_open_streams) || null,
@@ -37,7 +37,7 @@ function wGET(url, header, filepath) {
return exitcode;
}
const RES_TYPE = ['certs', 'provider', 'ruleset', 'resources', 'templates'];
const RES_TYPE = ['certs', 'node', 'provider', 'ruleset', 'resources', 'templates'];
const methods = {
mihomo_generator: {
@@ -330,7 +330,7 @@ const methods = {
},
file_read: {
args: { type: 'type', filename: 'filename' },
args: { type: 'type', filename: 'filename', isbinary: 'isbinary' },
call: function(req) {
if (!(req.args?.type in RES_TYPE))
return { content: null, error: 'illegal type' };
@@ -338,13 +338,17 @@ const methods = {
if ((!req.args?.filename) || match(req.args?.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 };
}
},
file_write: {
args: { type: 'type', filename: 'filename', content: 'content' },
args: { type: 'type', filename: 'filename', content: 'content', isbinary: 'isbinary' },
call: function(req) {
if (!(req.args?.type in RES_TYPE))
return { result: false, error: 'illegal type' };
@@ -363,6 +367,9 @@ const methods = {
content += '\n';
}
if (req.args?.isbinary == 1)
content = b64dec(content);
//system(`mkdir -p ${HM_DIR}/${req.args?.type}`);
writefile(file, content);
@@ -403,7 +410,7 @@ const methods = {
if ((!req.args?.filename) || match(req.args?.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 };
}
+2 -2
View File
@@ -2,8 +2,8 @@
include $(TOPDIR)/rules.mk
PKG_VERSION:=1.0.2-r20240822
PKG_RELEASE:=4
PKG_VERSION:=1.0.2-r20260804
PKG_RELEASE:=5
LUCI_TITLE:=LuCI support for FeiShuVpn
LUCI_PKGARCH:=all
@@ -35,7 +35,7 @@ o = s:option(Value, "port", translate("Port").."<b>*</b>")
o.default = "9091"
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.datatype = "string"
o.default = "registry.cn-qingdao.aliyuncs.com/feishuwg/p2p:latest"
+3
View File
@@ -13,6 +13,9 @@ msgstr "飞鼠组网是一个点对点的组网工具。"
msgid "Config path"
msgstr "配置文件路径"
msgid "Docker Image"
msgstr "镜像"
msgid "Port"
msgstr "端口"
+2 -2
View File
@@ -2,8 +2,8 @@
include $(TOPDIR)/rules.mk
PKG_VERSION:=1.0.3-r20240822
PKG_RELEASE:=4
PKG_VERSION:=1.0.3-r20260804
PKG_RELEASE:=5
LUCI_TITLE:=LuCI support for Gogs
LUCI_PKGARCH:=all
+1 -1
View File
@@ -28,7 +28,7 @@ o.default = "3022"
o.datatype = "string"
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.datatype = "string"
o:value("gogs/gogs:latest", "gogs/gogs:latest")
+3
View File
@@ -10,6 +10,9 @@ msgstr "Gogs 是一个轻松易用的 Git 服务。"
msgid "Config path"
msgstr "配置文件路径"
msgid "Docker Image"
msgstr "镜像"
msgid "HTTP Port"
msgstr "HTTP 端口"
+2 -2
View File
@@ -2,8 +2,8 @@
include $(TOPDIR)/rules.mk
PKG_VERSION:=1.1.4-r20250321
PKG_RELEASE:=4
PKG_VERSION:=1.1.4-r20260804
PKG_RELEASE:=5
LUCI_TITLE:=LuCI support for homeassistant
LUCI_PKGARCH:=all
@@ -18,7 +18,7 @@ s = m:section(TypedSection, "homeassistant", translate("Setup"), translate("The
s.addremove=false
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.datatype = "string"
o:value("homeassistant/home-assistant:latest", "homeassistant/home-assistant:latest")
@@ -10,6 +10,9 @@ msgstr "将本地控制和隐私放在首位的开源家庭自动化,由全世
msgid "Config path"
msgstr "配置文件路径"
msgid "Docker Image"
msgstr "镜像"
msgid "Service Status"
msgstr "服务状态"
+2 -2
View File
@@ -2,8 +2,8 @@
include $(TOPDIR)/rules.mk
PKG_VERSION:=1.0.2-r20240822
PKG_RELEASE:=4
PKG_VERSION:=1.0.2-r20260804
PKG_RELEASE:=5
LUCI_TITLE:=LuCI support for HTReader
LUCI_PKGARCH:=all
@@ -47,7 +47,7 @@ o = s:option(Value, "active_code", translate("Active code"))
o.datatype = "string"
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.datatype = "string"
o.default = "hectorqin/reader"
+3
View File
@@ -10,6 +10,9 @@ msgstr "HTReader 是一个网页版本在线读书。"
msgid "Config path"
msgstr "配置文件路径"
msgid "Docker Image"
msgstr "镜像"
msgid "Port"
msgstr "端口"
+2 -2
View File
@@ -2,8 +2,8 @@
include $(TOPDIR)/rules.mk
PKG_VERSION:=1.0.2-r20250321
PKG_RELEASE:=4
PKG_VERSION:=1.0.2-r20260804
PKG_RELEASE:=5
LUCI_TITLE:=LuCI support for Immich
LUCI_PKGARCH:=all
+1 -1
View File
@@ -35,7 +35,7 @@ o = s:option(Value, "port", translate("Port").."<b>*</b>")
o.default = "2283"
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.datatype = "string"
o.default = "release"
+3
View File
@@ -13,6 +13,9 @@ msgstr "Immich 是私有化的相册视频管理工具。很占资源,网络
msgid "Config path"
msgstr "配置文件路径"
msgid "Docker Image"
msgstr "镜像"
msgid "Port"
msgstr "端口"
+2 -2
View File
@@ -2,8 +2,8 @@
include $(TOPDIR)/rules.mk
PKG_VERSION:=1.1.7-r20241211
PKG_RELEASE:=4
PKG_VERSION:=1.1.7-r20260804
PKG_RELEASE:=5
LUCI_TITLE:=LuCI support for istoredup
LUCI_PKGARCH:=all
@@ -31,7 +31,7 @@ s = m:section(TypedSection, "istoredup", translate("Setup"),
s.addremove=false
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.datatype = "string"
o:value("linkease/istoreduprk35xx:latest", "linkease/istoreduprk35xx:latest")
+3
View File
@@ -7,6 +7,9 @@ msgstr "官方网站:"
msgid "A duplica of iStoreOS."
msgstr "iStoreOS 的分身,可以把风险高的软件安装到分身里面,出问题不影响到主系统。"
msgid "Docker Image"
msgstr "镜像"
msgid "Service Status"
msgstr "服务状态"
+2 -2
View File
@@ -2,8 +2,8 @@
include $(TOPDIR)/rules.mk
PKG_VERSION:=1.0.6-r20250406
PKG_RELEASE:=4
PKG_VERSION:=1.0.6-r20260804
PKG_RELEASE:=5
LUCI_TITLE:=LuCI support for 1Panel
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.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.datatype = "string"
o:value("linkease/istorepanel:latest", "linkease/istorepanel:latest")
@@ -10,6 +10,9 @@ msgstr "1Panel 是新一代的 Linux 服务器运维管理面板"
msgid "Config path"
msgstr "配置文件路径"
msgid "Docker Image"
msgstr "镜像"
msgid "Port"
msgstr "端口"
+2 -2
View File
@@ -2,8 +2,8 @@
include $(TOPDIR)/rules.mk
PKG_VERSION:=1.0.4-r20250321
PKG_RELEASE:=4
PKG_VERSION:=1.0.4-r20260804
PKG_RELEASE:=5
LUCI_TITLE:=LuCI support for ITTools
LUCI_PKGARCH:=all
@@ -35,7 +35,7 @@ o.default = "9070"
o.datatype = "port"
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.datatype = "string"
o.default = "corentinth/it-tools:latest"
+3
View File
@@ -1,6 +1,9 @@
msgid ""
msgstr "Content-Type: text/plain; charset=UTF-8"
msgid "Docker Image"
msgstr "镜像"
msgid "ITTools"
msgid "开发工具集"
+2 -2
View File
@@ -2,8 +2,8 @@
include $(TOPDIR)/rules.mk
PKG_VERSION:=1.1.1-r20240822
PKG_RELEASE:=4
PKG_VERSION:=1.1.1-r20260804
PKG_RELEASE:=5
LUCI_TITLE:=LuCI support for jackett
LUCI_PKGARCH:=all
@@ -23,7 +23,7 @@ o.rmempty = false
o.default = "9117"
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.datatype = "string"
o:value("linuxserver/jackett:latest", "linuxserver/jackett:latest")
+3
View File
@@ -10,6 +10,9 @@ msgstr "Jackett 是维护索引器抓取和翻译逻辑的单一存储库 - 消
msgid "Config path"
msgstr "配置文件路径"
msgid "Docker Image"
msgstr "镜像"
msgid "Torrent save path"
msgstr "种子保存路径"
+2 -2
View File
@@ -2,8 +2,8 @@
include $(TOPDIR)/rules.mk
PKG_VERSION:=1.2.2-r20250321
PKG_RELEASE:=4
PKG_VERSION:=1.2.2-r20260804
PKG_RELEASE:=5
LUCI_TITLE:=LuCI support for jellyfin
LUCI_PKGARCH:=all
@@ -27,7 +27,7 @@ s = m:section(TypedSection, "jellyfin", translate("Setup"),
s.addremove=false
s.anonymous=true
o = s:option(Value, "image", translate("Image"))
o = s:option(Value, "image", translate("Docker Image"))
o.datatype = "string"
o:value("", translate("Default"))
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"
msgstr "树莓派系列"
msgid "Image"
msgid "Docker Image"
msgstr "镜像"
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_PKGARCH:=all
PKG_VERSION:=20220109
PKG_RELEASE:=2
PKG_RELEASE:=3
include $(TOPDIR)/feeds/luci/luci.mk
@@ -1,19 +1,4 @@
#!/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 disable
+2 -2
View File
@@ -2,8 +2,8 @@
include $(TOPDIR)/rules.mk
PKG_VERSION:=1.0.1-r20240822
PKG_RELEASE:=4
PKG_VERSION:=1.0.1-r20260804
PKG_RELEASE:=5
LUCI_TITLE:=LuCI support for LANraragi
LUCI_PKGARCH:=all
@@ -24,7 +24,7 @@ o.rmempty = false
o.default = "3000"
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.datatype = "string"
o:value("dezhao/lanraragi_cn", "dezhao/lanraragi_cn")
+3
View File
@@ -13,6 +13,9 @@ msgstr "配置文件路径"
msgid "Content path"
msgstr "资源路径"
msgid "Docker Image"
msgstr "镜像"
msgid "HTTP Port"
msgstr "HTTP 端口"
+2 -2
View File
@@ -2,8 +2,8 @@
include $(TOPDIR)/rules.mk
PKG_VERSION:=1.0.2-r20240822
PKG_RELEASE:=4
PKG_VERSION:=1.0.2-r20260804
PKG_RELEASE:=5
LUCI_TITLE:=LuCI support for Memos
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.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.datatype = "string"
o:value("neosmemo/memos:latest", "neosmemo/memos:latest")
+3
View File
@@ -13,6 +13,9 @@ msgstr "Memos 是一个开源的极简的碎片化知识管理工具。"
msgid "Config path"
msgstr "配置文件路径"
msgid "Docker Image"
msgstr "镜像"
msgid "HTTP Port"
msgstr "HTTP 端口"
+2 -2
View File
@@ -2,8 +2,8 @@
include $(TOPDIR)/rules.mk
PKG_VERSION:=1.0.2-r20240822
PKG_RELEASE:=4
PKG_VERSION:=1.0.2-r20260804
PKG_RELEASE:=5
LUCI_TITLE:=LuCI support for MTPhotos
LUCI_PKGARCH:=all
@@ -36,7 +36,7 @@ o.default = "8063"
o.datatype = "port"
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.datatype = "string"
o:value("mtphotos/mt-photos:nodb-latest", "mtphotos/mt-photos:nodb-latest")
+3
View File
@@ -10,6 +10,9 @@ msgstr "MTPhotos 是一个相册管理软件。"
msgid "Config path"
msgstr "配置文件路径"
msgid "Docker Image"
msgstr "镜像"
msgid "Upload path"
msgstr "上传文件路径"
+2 -2
View File
@@ -2,8 +2,8 @@
include $(TOPDIR)/rules.mk
PKG_VERSION:=1.1.3-r20240822
PKG_RELEASE:=4
PKG_VERSION:=1.1.3-r20260804
PKG_RELEASE:=5
LUCI_TITLE:=LuCI support for nastools
LUCI_PKGARCH:=all
@@ -23,7 +23,7 @@ o.rmempty = false
o.default = "3003"
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.datatype = "string"
o:value("jxxghp/nas-tools", "jxxghp/nas-tools")
+3
View File
@@ -10,6 +10,9 @@ msgstr "NasTools 是汇聚了电影搜索,下载,订阅,观看等等 NAS
msgid "Config path"
msgstr "配置文件路径"
msgid "Docker Image"
msgstr "镜像"
msgid "Port"
msgstr "端口"
+2 -2
View File
@@ -2,8 +2,8 @@
include $(TOPDIR)/rules.mk
PKG_VERSION:=1.0.1-r20240822
PKG_RELEASE:=4
PKG_VERSION:=1.0.1-r20260804
PKG_RELEASE:=5
LUCI_TITLE:=LuCI support for Navidrome
LUCI_PKGARCH:=all
@@ -23,7 +23,7 @@ o.rmempty = false
o.default = "3000"
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.datatype = "string"
o:value("difegue/navidrome", "difegue/navidrome")
+3
View File
@@ -10,6 +10,9 @@ msgstr "Navidrome 是一个开源的在线音乐播放软件。"
msgid "Config path"
msgstr "配置文件路径"
msgid "Docker Image"
msgstr "镜像"
msgid "Music path"
msgstr "音乐路径"
+2 -2
View File
@@ -2,8 +2,8 @@
include $(TOPDIR)/rules.mk
PKG_VERSION:=1.1.1-r20240822
PKG_RELEASE:=4
PKG_VERSION:=1.1.1-r20260804
PKG_RELEASE:=5
LUCI_TITLE:=LuCI support for nextcloud
LUCI_PKGARCH:=all
@@ -23,7 +23,7 @@ o.rmempty = false
o.default = "8082"
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.datatype = "string"
o:value("nextcloud", "nextcloud")
+3
View File
@@ -10,6 +10,9 @@ msgstr "所有数据的安全之家。 根据您的条件,从任何设备访
msgid "Config path"
msgstr "配置文件路径"
msgid "Docker Image"
msgstr "镜像"
msgid "Port"
msgstr "端口"
+1 -1
View File
@@ -1,7 +1,7 @@
include $(TOPDIR)/rules.mk
PKG_VERSION:=1.26.1
PKG_RELEASE:=7
PKG_RELEASE:=8
LUCI_TITLE:=LuCI Support for nikki
LUCI_DEPENDS:=+luci-base +nikki
@@ -82,7 +82,7 @@ const methods = {
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}'`);
if (process) {
+2 -2
View File
@@ -2,8 +2,8 @@
include $(TOPDIR)/rules.mk
PKG_VERSION:=1.0.1-r20250403
PKG_RELEASE:=4
PKG_VERSION:=1.0.1-r20260804
PKG_RELEASE:=5
LUCI_TITLE:=LuCI support for OneAPI
LUCI_PKGARCH:=all
+1 -1
View File
@@ -36,7 +36,7 @@ o.default = "3005"
o.datatype = "port"
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.datatype = "string"
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"
msgstr "配置文件路径"
msgid "Docker Image"
msgstr "镜像"
msgid "Port"
msgstr "端口"
+2 -2
View File
@@ -2,8 +2,8 @@
include $(TOPDIR)/rules.mk
PKG_VERSION:=1.0.8-r20250207
PKG_RELEASE:=4
PKG_VERSION:=1.0.8-r20260804
PKG_RELEASE:=5
LUCI_TITLE:=LuCI support for OpenWebUI
LUCI_PKGARCH:=all
@@ -35,7 +35,7 @@ o = s:option(Value, "port", translate("Port").."<b>*</b>")
o.default = "3000"
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.datatype = "string"
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"
msgstr "配置文件路径"
msgid "Docker Image"
msgstr "镜像"
msgid "Port"
msgstr "端口"
+2 -2
View File
@@ -2,8 +2,8 @@
include $(TOPDIR)/rules.mk
PKG_VERSION:=1.0.2-r20240822
PKG_RELEASE:=4
PKG_VERSION:=1.0.2-r20260804
PKG_RELEASE:=5
LUCI_TITLE:=LuCI support for Owntone
LUCI_PKGARCH:=all
@@ -18,7 +18,7 @@ s = m:section(TypedSection, "main", translate("Setup"), translate("The following
s.addremove=false
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.datatype = "string"
o:value("lscr.io/linuxserver/daapd:latest", "lscr.io/linuxserver/daapd:latest")

Some files were not shown because too many files have changed in this diff Show More