Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
344aea7ec2 | ||
|
|
4a4dfac1a2 |
@@ -6,7 +6,7 @@ include $(TOPDIR)/rules.mk
|
|||||||
|
|
||||||
PKG_NAME:=dae
|
PKG_NAME:=dae
|
||||||
PKG_VERSION:=2026.08.28
|
PKG_VERSION:=2026.08.28
|
||||||
PKG_RELEASE:=4
|
PKG_RELEASE:=6
|
||||||
|
|
||||||
PKG_SOURCE:=dae-src-2026.08.28-d6aca8a10b35.tar.gz
|
PKG_SOURCE:=dae-src-2026.08.28-d6aca8a10b35.tar.gz
|
||||||
PKG_SOURCE_URL:=https://github.com/kenzok8/openwrt-daede/releases/download/dae-src
|
PKG_SOURCE_URL:=https://github.com/kenzok8/openwrt-daede/releases/download/dae-src
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ include $(TOPDIR)/rules.mk
|
|||||||
|
|
||||||
PKG_NAME:=daed
|
PKG_NAME:=daed
|
||||||
PKG_VERSION:=2026.08.28
|
PKG_VERSION:=2026.08.28
|
||||||
PKG_RELEASE:=4
|
PKG_RELEASE:=6
|
||||||
|
|
||||||
PKG_SOURCE:=daed-src-2026.08.28-73f01b995c34.tar.gz
|
PKG_SOURCE:=daed-src-2026.08.28-73f01b995c34.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
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 91 KiB |
|
Before Width: | Height: | Size: 319 KiB |
|
Before Width: | Height: | Size: 389 KiB |
|
After Width: | Height: | Size: 640 KiB |
|
After Width: | Height: | Size: 550 KiB |
|
After Width: | Height: | Size: 547 KiB |
|
After Width: | Height: | Size: 659 KiB |
|
After Width: | Height: | Size: 650 KiB |
@@ -9,7 +9,7 @@ include $(TOPDIR)/rules.mk
|
|||||||
|
|
||||||
PKG_NAME:=automount
|
PKG_NAME:=automount
|
||||||
PKG_VERSION:=1
|
PKG_VERSION:=1
|
||||||
PKG_RELEASE:=40
|
PKG_RELEASE:=41
|
||||||
PKG_ARCH:=all
|
PKG_ARCH:=all
|
||||||
|
|
||||||
include $(INCLUDE_DIR)/package.mk
|
include $(INCLUDE_DIR)/package.mk
|
||||||
|
|||||||
@@ -6,6 +6,9 @@
|
|||||||
blkdev=`dirname $DEVPATH`
|
blkdev=`dirname $DEVPATH`
|
||||||
basename=`basename $blkdev`
|
basename=`basename $blkdev`
|
||||||
device=`basename $DEVPATH`
|
device=`basename $DEVPATH`
|
||||||
|
|
||||||
|
grep -qs "^/dev/$device " /proc/mounts && exit 0
|
||||||
|
|
||||||
skip=`block info | grep -vE '(f2fs|kernel|squashfs)' | sed 's/\(.*\): .*/\1/' | grep -q $device ; echo $?`
|
skip=`block info | grep -vE '(f2fs|kernel|squashfs)' | sed 's/\(.*\): .*/\1/' | grep -q $device ; echo $?`
|
||||||
path=$DEVPATH
|
path=$DEVPATH
|
||||||
|
|
||||||
|
|||||||
@@ -5,8 +5,8 @@
|
|||||||
include $(TOPDIR)/rules.mk
|
include $(TOPDIR)/rules.mk
|
||||||
|
|
||||||
PKG_NAME:=luci-app-daede
|
PKG_NAME:=luci-app-daede
|
||||||
PKG_VERSION:=1.14.7
|
PKG_VERSION:=1.15
|
||||||
PKG_RELEASE:=25
|
PKG_RELEASE:=2
|
||||||
PKG_MAINTAINER:=kenzok8
|
PKG_MAINTAINER:=kenzok8
|
||||||
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)
|
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)
|
||||||
|
|
||||||
@@ -78,6 +78,7 @@ define Package/$(PKG_NAME)/install
|
|||||||
$(INSTALL_BIN) ./root/usr/share/luci-app-daede/proxy-check.sh $(1)/usr/share/luci-app-daede/proxy-check.sh
|
$(INSTALL_BIN) ./root/usr/share/luci-app-daede/proxy-check.sh $(1)/usr/share/luci-app-daede/proxy-check.sh
|
||||||
$(INSTALL_BIN) ./root/usr/share/luci-app-daede/fetch-clash-yaml.sh $(1)/usr/share/luci-app-daede/fetch-clash-yaml.sh
|
$(INSTALL_BIN) ./root/usr/share/luci-app-daede/fetch-clash-yaml.sh $(1)/usr/share/luci-app-daede/fetch-clash-yaml.sh
|
||||||
$(INSTALL_BIN) ./root/usr/share/luci-app-daede/config-backup.sh $(1)/usr/share/luci-app-daede/config-backup.sh
|
$(INSTALL_BIN) ./root/usr/share/luci-app-daede/config-backup.sh $(1)/usr/share/luci-app-daede/config-backup.sh
|
||||||
|
$(INSTALL_BIN) ./root/usr/share/luci-app-daede/snapshot-file.sh $(1)/usr/share/luci-app-daede/snapshot-file.sh
|
||||||
$(INSTALL_BIN) ./root/usr/share/luci-app-daede/config-defaults.sh $(1)/usr/share/luci-app-daede/config-defaults.sh
|
$(INSTALL_BIN) ./root/usr/share/luci-app-daede/config-defaults.sh $(1)/usr/share/luci-app-daede/config-defaults.sh
|
||||||
$(INSTALL_DIR) $(1)/usr/share/luci-app-daede/defaults
|
$(INSTALL_DIR) $(1)/usr/share/luci-app-daede/defaults
|
||||||
$(INSTALL_DATA) $(CURDIR)/../dae/files/dae.config $(1)/usr/share/luci-app-daede/defaults/dae
|
$(INSTALL_DATA) $(CURDIR)/../dae/files/dae.config $(1)/usr/share/luci-app-daede/defaults/dae
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
|
|
||||||
const FETCHER = '/usr/share/luci-app-daede/fetch-clash-yaml.sh';
|
const FETCHER = '/usr/share/luci-app-daede/fetch-clash-yaml.sh';
|
||||||
const GENERATOR = '/usr/share/luci-app-daede/gen-dae-config.sh';
|
const GENERATOR = '/usr/share/luci-app-daede/gen-dae-config.sh';
|
||||||
|
const SNAPSHOT_HELPER = '/usr/share/luci-app-daede/snapshot-file.sh';
|
||||||
const SUB_STAGE = '/tmp/daede-sub.txt';
|
const SUB_STAGE = '/tmp/daede-sub.txt';
|
||||||
const FETCH_CHUNK_BYTES = 16384;
|
const FETCH_CHUNK_BYTES = 16384;
|
||||||
|
|
||||||
@@ -651,11 +652,19 @@ return view.extend({
|
|||||||
const name = airportName.value.trim();
|
const name = airportName.value.trim();
|
||||||
const groupName = airportSync.backendGroupName(name, 'daed', airportId);
|
const groupName = airportSync.backendGroupName(name, 'daed', airportId);
|
||||||
const endpoint = daedEndpoint();
|
const endpoint = daedEndpoint();
|
||||||
// daed can't read file:// — it HTTP-fetches a subscription link. Stage
|
// daed can't read file:// — it HTTP-fetches a subscription link. Persist
|
||||||
// the converted links (base64) and let daed pull them from the
|
// the converted snapshot and let daed pull it from the loopback-only CGI,
|
||||||
// loopback-only CGI, so the import lands as ONE subscription.
|
// so later daed refreshes and configuration backups keep working.
|
||||||
const subToken = airportSync.backendId(airportId) + Date.now().toString(36);
|
// Keep the CGI token opaque, bounded, and unique across imports started in
|
||||||
const stageFile = '/tmp/daede-daedsub-' + subToken;
|
// the same millisecond. The final sanitization also protects the filename
|
||||||
|
// and the CGI's allow-list if backendId ever changes.
|
||||||
|
const tokenTime = Date.now().toString(36);
|
||||||
|
const tokenRandom = Math.random().toString(36).slice(2, 12);
|
||||||
|
const tokenPrefix = airportSync.backendId(airportId).replace(/[^A-Za-z0-9]/g, '')
|
||||||
|
.slice(0, Math.max(0, 64 - tokenTime.length - tokenRandom.length));
|
||||||
|
const subToken = (tokenPrefix + tokenTime + tokenRandom).slice(0, 64);
|
||||||
|
const snapshotFile = '/etc/daed/daede-sub-' + subToken;
|
||||||
|
const stageFile = '/etc/daed/.daede-sub-stage-' + subToken;
|
||||||
const subUrl = 'http://127.0.0.1/cgi-bin/daede-sub?t=' + subToken;
|
const subUrl = 'http://127.0.0.1/cgi-bin/daede-sub?t=' + subToken;
|
||||||
const b64 = btoa(items.map(function(item) { return item.link; }).join('\n') + '\n');
|
const b64 = btoa(items.map(function(item) { return item.link; }).join('\n') + '\n');
|
||||||
|
|
||||||
@@ -664,15 +673,156 @@ return view.extend({
|
|||||||
let createdGroupId = '';
|
let createdGroupId = '';
|
||||||
const createdNodeIds = [];
|
const createdNodeIds = [];
|
||||||
let groupReady = false;
|
let groupReady = false;
|
||||||
|
let snapshotCommitted = false;
|
||||||
|
let snapshotFinalToken = '';
|
||||||
|
let reuseExistingSnapshot = false;
|
||||||
const oldSubId = existingAirport ? existingAirport.subscription_id : '';
|
const oldSubId = existingAirport ? existingAirport.subscription_id : '';
|
||||||
const oldNodeIds = existingAirport ? existingAirport.node_ids : [];
|
const oldNodeIds = existingAirport ? existingAirport.node_ids : [];
|
||||||
|
|
||||||
const dropStage = function() { return fs.exec('/bin/rm', [ '-f', stageFile ]).catch(function() {}); };
|
const dropSnapshot = function(path) {
|
||||||
|
if (!path)
|
||||||
|
return Promise.resolve();
|
||||||
|
return fs.remove(path).catch(function(error) {
|
||||||
|
const message = String(error && (error.message || error) || '');
|
||||||
|
if (/not found|no such file|enoent/i.test(message))
|
||||||
|
return;
|
||||||
|
throw error;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
const appendWarning = function(error, warning) {
|
||||||
|
const result = error instanceof Error ? error : new Error(String(error && (error.message || error) || error));
|
||||||
|
result.message = result.message + '; ' + warning;
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
const snapshotAction = function(args) {
|
||||||
|
return fs.exec(SNAPSHOT_HELPER, args).then(function(res) {
|
||||||
|
if (!res || res.code !== 0)
|
||||||
|
throw new Error((res && (res.stderr || res.stdout)) || _('Snapshot operation failed'));
|
||||||
|
return res;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
const cleanupStage = function() {
|
||||||
|
return snapshotAction([ 'discard', subToken ]);
|
||||||
|
};
|
||||||
|
const querySubscriptions = function() {
|
||||||
|
if (!token)
|
||||||
|
return Promise.reject(new Error(_('daed subscription cleanup could not be verified')));
|
||||||
|
return graphQL(endpoint, 'query SubscriptionLinks{subscriptions{id link}}', {}, token).then(function(value) {
|
||||||
|
if (!value || !Array.isArray(value.subscriptions))
|
||||||
|
throw new Error(_('daed returned an invalid subscription list'));
|
||||||
|
return value.subscriptions;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
const dropSnapshotIfUnreferenced = function() {
|
||||||
|
if (snapshotFinalToken !== subToken)
|
||||||
|
return Promise.resolve();
|
||||||
|
return querySubscriptions().then(function(subscriptions) {
|
||||||
|
if (subscriptions.some(function(sub) { return String(sub.link || '') === subUrl; }))
|
||||||
|
throw new Error(_('new daed subscription snapshot is still referenced; it was kept'));
|
||||||
|
return dropSnapshot(snapshotFile);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
const finishWithSnapshotCleanup = function(result) {
|
||||||
|
return dropSnapshotIfUnreferenced().then(function() {
|
||||||
|
return result;
|
||||||
|
}).catch(function(error) {
|
||||||
|
const warning = String(error && (error.message || error) || error);
|
||||||
|
result.warning = result.warning
|
||||||
|
? result.warning + '; ' + warning
|
||||||
|
: warning;
|
||||||
|
return result;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
const updateExistingSubscription = function() {
|
||||||
|
const old = (((before || {}).subscriptions) || []).find(function(sub) { return sub.id === oldSubId; });
|
||||||
|
if (!old)
|
||||||
|
return Promise.reject(new Error(_('Managed subscription no longer exists in daed')));
|
||||||
|
|
||||||
|
const oldLink = String(old.link || '');
|
||||||
|
const localToken = oldLink.match(/^http:\/\/127\.0\.0\.1\/cgi-bin\/daede-sub\?t=([A-Za-z0-9]{1,64})$/);
|
||||||
|
const refresh = function() {
|
||||||
|
return graphQL(endpoint, 'mutation UpdateSub($id:ID!){updateSubscription(id:$id){id}}', { id: oldSubId }, token);
|
||||||
|
};
|
||||||
|
const currentLink = function() {
|
||||||
|
return querySubscriptions().then(function(subscriptions) {
|
||||||
|
const current = subscriptions.find(function(sub) { return sub.id === oldSubId; });
|
||||||
|
return current ? String(current.link || '') : '';
|
||||||
|
});
|
||||||
|
};
|
||||||
|
const restoreOldLink = function(originalError) {
|
||||||
|
return graphQL(endpoint, 'mutation RestoreLink($id:ID!,$link:String!){updateSubscriptionLink(id:$id,link:$link)}', {
|
||||||
|
id: oldSubId,
|
||||||
|
link: oldLink
|
||||||
|
}, token).then(refresh).then(currentLink).then(function(link) {
|
||||||
|
if (link !== oldLink) {
|
||||||
|
snapshotCommitted = true;
|
||||||
|
throw appendWarning(originalError, _('rollback was not confirmed; both subscription snapshots were kept'));
|
||||||
|
}
|
||||||
|
throw originalError;
|
||||||
|
}, function(rollbackError) {
|
||||||
|
snapshotCommitted = true;
|
||||||
|
throw appendWarning(originalError, _('rollback could not be confirmed; both subscription snapshots were kept: %s').format(String(rollbackError && (rollbackError.message || rollbackError) || rollbackError)));
|
||||||
|
});
|
||||||
|
};
|
||||||
|
const finish = function() {
|
||||||
|
const tag = old.tag !== groupName
|
||||||
|
? graphQL(endpoint, 'mutation TagSub($id:ID!,$tag:String!){tagSubscription(id:$id,tag:$tag)}', {
|
||||||
|
id: oldSubId,
|
||||||
|
tag: groupName
|
||||||
|
}, token).catch(function(error) {
|
||||||
|
return String(error && (error.message || error) || error);
|
||||||
|
})
|
||||||
|
: Promise.resolve('');
|
||||||
|
return tag.then(function(tagWarning) {
|
||||||
|
writeAirportRecord(existingAirport, {
|
||||||
|
id: airportId,
|
||||||
|
backend: 'daed',
|
||||||
|
name: name,
|
||||||
|
sourceHash: state.sourceHash,
|
||||||
|
groupId: existingAirport.group_id,
|
||||||
|
subscriptionId: oldSubId,
|
||||||
|
nodeIds: []
|
||||||
|
});
|
||||||
|
return applyUciChanges().then(function() {
|
||||||
|
items.forEach(function(item) { item.duplicate = true; item.selected = false; });
|
||||||
|
return {
|
||||||
|
added: items.length,
|
||||||
|
duplicates: 0,
|
||||||
|
failed: 0,
|
||||||
|
warning: tagWarning
|
||||||
|
};
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
if (localToken && reuseExistingSnapshot) {
|
||||||
|
return graphQL(endpoint, 'mutation UpdateSub($id:ID!){updateSubscription(id:$id){id}}', { id: oldSubId }, token)
|
||||||
|
.then(currentLink).then(function(link) {
|
||||||
|
if (link !== oldLink)
|
||||||
|
throw new Error(_('daed did not confirm the existing subscription link after refresh'));
|
||||||
|
}).catch(function(error) {
|
||||||
|
throw appendWarning(error, _('daed subscription refresh failed; the new snapshot remains at the existing link and existing nodes were kept'));
|
||||||
|
}).then(finish);
|
||||||
|
}
|
||||||
|
|
||||||
|
return graphQL(endpoint, 'mutation SetLink($id:ID!,$link:String!){updateSubscriptionLink(id:$id,link:$link)}', {
|
||||||
|
id: oldSubId,
|
||||||
|
link: subUrl
|
||||||
|
}, token).then(refresh).then(function() {
|
||||||
|
return currentLink().then(function(link) {
|
||||||
|
if (link !== subUrl)
|
||||||
|
throw new Error(_('daed did not confirm the new subscription link'));
|
||||||
|
});
|
||||||
|
}).catch(restoreOldLink).then(function() {
|
||||||
|
snapshotCommitted = true;
|
||||||
|
return finish();
|
||||||
|
});
|
||||||
|
};
|
||||||
const loadState = function(forceLogin) {
|
const loadState = function(forceLogin) {
|
||||||
return requestDaedToken(endpoint, forceLogin).then(function(auth) {
|
return requestDaedToken(endpoint, forceLogin).then(function(auth) {
|
||||||
token = auth.token;
|
token = auth.token;
|
||||||
usedCachedToken = auth.cached;
|
usedCachedToken = auth.cached;
|
||||||
return graphQL(endpoint, 'query State{nodes(first:10000){edges{id link tag}} groups{id name nodes{id}}}', {}, token);
|
return graphQL(endpoint, 'query State{nodes(first:10000){edges{id link tag}} groups{id name nodes{id}} subscriptions{id link tag}}', {}, token);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
const importDirectNodes = function() {
|
const importDirectNodes = function() {
|
||||||
@@ -797,7 +947,7 @@ return view.extend({
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
return fs.write(stageFile, b64).then(function() {
|
return fs.write(stageFile, b64, 384).then(function() {
|
||||||
return loadState(false).catch(function(error) {
|
return loadState(false).catch(function(error) {
|
||||||
if (!usedCachedToken || !daedSession.isAccessDenied(error))
|
if (!usedCachedToken || !daedSession.isAccessDenied(error))
|
||||||
throw error;
|
throw error;
|
||||||
@@ -806,42 +956,63 @@ return view.extend({
|
|||||||
});
|
});
|
||||||
}).then(function(dataValue) {
|
}).then(function(dataValue) {
|
||||||
before = dataValue;
|
before = dataValue;
|
||||||
// drop this airport's previous subscription first: the new one
|
const old = (before.subscriptions || []).find(function(sub) { return sub.id === oldSubId; });
|
||||||
// reuses the same tag (group name) and daed enforces unique tags.
|
const localToken = old && String(old.link || '').match(/^http:\/\/127\.0\.0\.1\/cgi-bin\/daede-sub\?t=([A-Za-z0-9]{1,64})$/);
|
||||||
if (!oldSubId)
|
const oldLinkReferences = localToken
|
||||||
return null;
|
? (before.subscriptions || []).filter(function(sub) { return String(sub.link || '') === String(old.link || ''); })
|
||||||
return graphQL(endpoint, 'mutation RmSub($ids:[ID!]!){removeSubscriptions(ids:$ids)}', { ids: [ oldSubId ] }, token).catch(function() {});
|
: [];
|
||||||
}).then(function() {
|
reuseExistingSnapshot = !!(localToken && oldLinkReferences.length === 1 && String(oldLinkReferences[0].id) === String(oldSubId));
|
||||||
// import the whole converted batch as one subscription
|
const publish = reuseExistingSnapshot
|
||||||
return graphQL(endpoint,
|
? snapshotAction([ 'replace', localToken[1], subToken ]).then(function() {
|
||||||
'mutation Import($a:ImportArgument!){importSubscription(rollbackError:false,arg:$a){sub{id} nodeImportResult{error}}}',
|
snapshotFinalToken = localToken[1];
|
||||||
{ a: { link: subUrl, tag: groupName } }, token).then(function(result) {
|
snapshotCommitted = true;
|
||||||
const sub = result.importSubscription && result.importSubscription.sub;
|
})
|
||||||
const rows = (result.importSubscription && result.importSubscription.nodeImportResult) || [];
|
: snapshotAction([ 'publish', subToken ]).then(function() {
|
||||||
const usable = rows.length
|
snapshotFinalToken = subToken;
|
||||||
? rows.some(function(r) { return !r.error || r.error === 'node already exists'; })
|
|
||||||
: !!(sub && sub.id);
|
|
||||||
if (!sub || !sub.id || !usable) {
|
|
||||||
const details = rows.map(function(r) { return r.error; }).filter(Boolean).join('; ');
|
|
||||||
if (sub && sub.id)
|
|
||||||
newSubId = sub.id;
|
|
||||||
throw new Error(details || _('No usable nodes were imported'));
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}).catch(function(error) {
|
|
||||||
if (daedSession.isAccessDenied(error))
|
|
||||||
throw error;
|
|
||||||
const cleanupSub = newSubId
|
|
||||||
? graphQL(endpoint, 'mutation Rm($ids:[ID!]!){removeSubscriptions(ids:$ids)}', { ids: [ newSubId ] }, token).catch(function() {})
|
|
||||||
: Promise.resolve();
|
|
||||||
newSubId = '';
|
|
||||||
return cleanupSub.then(importDirectNodes).then(function(result) {
|
|
||||||
return { directResult: result };
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
return publish.then(function() {
|
||||||
|
if (oldSubId)
|
||||||
|
return updateExistingSubscription().then(function(result) { return { existingResult: result }; });
|
||||||
|
// import the whole converted batch as one subscription
|
||||||
|
return graphQL(endpoint,
|
||||||
|
'mutation Import($a:ImportArgument!){importSubscription(rollbackError:false,arg:$a){sub{id} nodeImportResult{error}}}',
|
||||||
|
{ a: { link: subUrl, tag: groupName } }, token).then(function(result) {
|
||||||
|
const sub = result.importSubscription && result.importSubscription.sub;
|
||||||
|
const rows = (result.importSubscription && result.importSubscription.nodeImportResult) || [];
|
||||||
|
const usable = rows.length
|
||||||
|
? rows.some(function(r) { return !r.error || r.error === 'node already exists'; })
|
||||||
|
: !!(sub && sub.id);
|
||||||
|
if (!sub || !sub.id || !usable) {
|
||||||
|
const details = rows.map(function(r) { return r.error; }).filter(Boolean).join('; ');
|
||||||
|
if (sub && sub.id)
|
||||||
|
newSubId = sub.id;
|
||||||
|
throw new Error(details || _('No usable nodes were imported'));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}).catch(function(error) {
|
||||||
|
if (daedSession.isAccessDenied(error))
|
||||||
|
throw error;
|
||||||
|
const cleanupSub = newSubId
|
||||||
|
? graphQL(endpoint, 'mutation Rm($ids:[ID!]!){removeSubscriptions(ids:$ids)}', { ids: [ newSubId ] }, token).catch(function(cleanupError) {
|
||||||
|
snapshotCommitted = true;
|
||||||
|
throw appendWarning(error, _('rollback could not be confirmed; the new subscription snapshot was kept: %s').format(String(cleanupError && (cleanupError.message || cleanupError) || cleanupError)));
|
||||||
|
})
|
||||||
|
: Promise.resolve();
|
||||||
|
newSubId = '';
|
||||||
|
return cleanupSub.then(function() {
|
||||||
|
if (oldSubId)
|
||||||
|
throw error;
|
||||||
|
return importDirectNodes();
|
||||||
|
}).then(function(result) {
|
||||||
|
return { directResult: result };
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
}).then(function(result) {
|
}).then(function(result) {
|
||||||
|
if (result.existingResult)
|
||||||
|
return result.existingResult;
|
||||||
if (result.directResult)
|
if (result.directResult)
|
||||||
return result.directResult;
|
return finishWithSnapshotCleanup(result.directResult);
|
||||||
|
|
||||||
const sub = result.importSubscription && result.importSubscription.sub;
|
const sub = result.importSubscription && result.importSubscription.sub;
|
||||||
newSubId = sub.id;
|
newSubId = sub.id;
|
||||||
@@ -861,6 +1032,7 @@ return view.extend({
|
|||||||
return ensureGroup.then(function(groupId) {
|
return ensureGroup.then(function(groupId) {
|
||||||
return graphQL(endpoint, 'mutation AddSubs($id:ID!,$ids:[ID!]!){groupAddSubscriptions(id:$id,subscriptionIDs:$ids)}', { id: groupId, ids: [ newSubId ] }, token).then(function() {
|
return graphQL(endpoint, 'mutation AddSubs($id:ID!,$ids:[ID!]!){groupAddSubscriptions(id:$id,subscriptionIDs:$ids)}', { id: groupId, ids: [ newSubId ] }, token).then(function() {
|
||||||
groupReady = true;
|
groupReady = true;
|
||||||
|
snapshotCommitted = true;
|
||||||
const cleanup = [];
|
const cleanup = [];
|
||||||
// Migrate converter-managed airport groups to proxy, but never
|
// Migrate converter-managed airport groups to proxy, but never
|
||||||
// disturb proxy's existing nodes or other subscriptions.
|
// disturb proxy's existing nodes or other subscriptions.
|
||||||
@@ -880,7 +1052,12 @@ return view.extend({
|
|||||||
});
|
});
|
||||||
return applyUciChanges().then(function() {
|
return applyUciChanges().then(function() {
|
||||||
items.forEach(function(item) { item.duplicate = true; item.selected = false; });
|
items.forEach(function(item) { item.duplicate = true; item.selected = false; });
|
||||||
return { added: items.length, duplicates: 0, failed: failed };
|
return {
|
||||||
|
added: items.length,
|
||||||
|
duplicates: 0,
|
||||||
|
failed: failed,
|
||||||
|
warning: ''
|
||||||
|
};
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -892,15 +1069,47 @@ return view.extend({
|
|||||||
throw error;
|
throw error;
|
||||||
const cleanup = [];
|
const cleanup = [];
|
||||||
if (newSubId)
|
if (newSubId)
|
||||||
cleanup.push(graphQL(endpoint, 'mutation Rm($ids:[ID!]!){removeSubscriptions(ids:$ids)}', { ids: [ newSubId ] }, token));
|
cleanup.push(graphQL(endpoint, 'mutation Rm($ids:[ID!]!){removeSubscriptions(ids:$ids)}', { ids: [ newSubId ] }, token).catch(function(cleanupError) {
|
||||||
|
snapshotCommitted = true;
|
||||||
|
throw cleanupError;
|
||||||
|
}));
|
||||||
if (createdNodeIds.length)
|
if (createdNodeIds.length)
|
||||||
cleanup.push(graphQL(endpoint, 'mutation Rm($ids:[ID!]!){removeNodes(ids:$ids)}', { ids: createdNodeIds }, token));
|
cleanup.push(graphQL(endpoint, 'mutation Rm($ids:[ID!]!){removeNodes(ids:$ids)}', { ids: createdNodeIds }, token));
|
||||||
if (createdGroupId)
|
if (createdGroupId)
|
||||||
cleanup.push(graphQL(endpoint, 'mutation RmG($id:ID!){removeGroup(id:$id)}', { id: createdGroupId }, token));
|
cleanup.push(graphQL(endpoint, 'mutation RmG($id:ID!){removeGroup(id:$id)}', { id: createdGroupId }, token));
|
||||||
return Promise.all(cleanup).catch(function() {}).then(function() { throw error; });
|
return Promise.all(cleanup).then(function() {
|
||||||
}).finally(function() {
|
throw error;
|
||||||
token = '';
|
}, function(cleanupError) {
|
||||||
return dropStage();
|
snapshotCommitted = true;
|
||||||
|
throw appendWarning(error, _('rollback could not be confirmed; the new subscription snapshot was kept: %s').format(String(cleanupError && (cleanupError.message || cleanupError) || cleanupError)));
|
||||||
|
});
|
||||||
|
}).then(function(result) {
|
||||||
|
return cleanupStage().then(function() {
|
||||||
|
token = '';
|
||||||
|
return result;
|
||||||
|
}, function(cleanupError) {
|
||||||
|
token = '';
|
||||||
|
result.warning = result.warning
|
||||||
|
? result.warning + '; ' + String(cleanupError && (cleanupError.message || cleanupError) || cleanupError)
|
||||||
|
: String(cleanupError && (cleanupError.message || cleanupError) || cleanupError);
|
||||||
|
return result;
|
||||||
|
});
|
||||||
|
}, function(error) {
|
||||||
|
let cleanupWarning = '';
|
||||||
|
const removeSnapshot = snapshotCommitted
|
||||||
|
? Promise.resolve()
|
||||||
|
: dropSnapshotIfUnreferenced().catch(function(cleanupError) {
|
||||||
|
cleanupWarning = _('rollback could not be confirmed; the new subscription snapshot was kept: %s').format(String(cleanupError && (cleanupError.message || cleanupError) || cleanupError));
|
||||||
|
});
|
||||||
|
return removeSnapshot.then(function() {
|
||||||
|
return cleanupStage().catch(function(stageError) {
|
||||||
|
const warning = _('snapshot stage cleanup failed: %s').format(String(stageError && (stageError.message || stageError) || stageError));
|
||||||
|
cleanupWarning = cleanupWarning ? cleanupWarning + '; ' + warning : warning;
|
||||||
|
});
|
||||||
|
}).then(function() {
|
||||||
|
token = '';
|
||||||
|
throw cleanupWarning ? appendWarning(error, cleanupWarning) : error;
|
||||||
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -924,8 +1133,11 @@ return view.extend({
|
|||||||
setImportStatus(_('Importing node group…'));
|
setImportStatus(_('Importing node group…'));
|
||||||
const action = state.target === 'dae' ? importDae(items) : importDaed(items);
|
const action = state.target === 'dae' ? importDae(items) : importDaed(items);
|
||||||
action.then(function(result) {
|
action.then(function(result) {
|
||||||
setImportStatus(_('Node group imported: added %d, reused %d, failed %d')
|
let message = _('Node group imported: added %d, reused %d, failed %d')
|
||||||
.format(result.added, result.duplicates, result.failed), result.failed ? 'err' : 'ok');
|
.format(result.added, result.duplicates, result.failed);
|
||||||
|
if (result.warning)
|
||||||
|
message += ' ' + _('Completed with warning: %s').format(result.warning);
|
||||||
|
setImportStatus(message, result.failed || result.warning ? 'err' : 'ok');
|
||||||
renderResults();
|
renderResults();
|
||||||
}).catch(function(e) {
|
}).catch(function(e) {
|
||||||
setImportStatus(_('Node group import failed: %s').format(e.message || e), 'err');
|
setImportStatus(_('Node group import failed: %s').format(e.message || e), 'err');
|
||||||
|
|||||||
@@ -812,6 +812,12 @@ msgstr ""
|
|||||||
msgid "Node group imported: added %d, reused %d, failed %d"
|
msgid "Node group imported: added %d, reused %d, failed %d"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Completed with warning: %s"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Managed subscription no longer exists in daed"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Node group import failed: %s"
|
msgid "Node group import failed: %s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|||||||
@@ -1004,6 +1004,12 @@ msgstr "正在导入节点组…"
|
|||||||
msgid "Node group imported: added %d, reused %d, failed %d"
|
msgid "Node group imported: added %d, reused %d, failed %d"
|
||||||
msgstr "节点组导入完成:新增 %d,复用 %d,失败 %d"
|
msgstr "节点组导入完成:新增 %d,复用 %d,失败 %d"
|
||||||
|
|
||||||
|
msgid "Completed with warning: %s"
|
||||||
|
msgstr "已完成,但有警告:%s"
|
||||||
|
|
||||||
|
msgid "Managed subscription no longer exists in daed"
|
||||||
|
msgstr "daed 中已不存在该托管订阅"
|
||||||
|
|
||||||
msgid "Node group import failed: %s"
|
msgid "Node group import failed: %s"
|
||||||
msgstr "节点组导入失败:%s"
|
msgstr "节点组导入失败:%s"
|
||||||
|
|
||||||
|
|||||||
@@ -1004,6 +1004,12 @@ msgstr "正在导入节点组…"
|
|||||||
msgid "Node group imported: added %d, reused %d, failed %d"
|
msgid "Node group imported: added %d, reused %d, failed %d"
|
||||||
msgstr "节点组导入完成:新增 %d,复用 %d,失败 %d"
|
msgstr "节点组导入完成:新增 %d,复用 %d,失败 %d"
|
||||||
|
|
||||||
|
msgid "Completed with warning: %s"
|
||||||
|
msgstr "已完成,但有警告:%s"
|
||||||
|
|
||||||
|
msgid "Managed subscription no longer exists in daed"
|
||||||
|
msgstr "daed 中已不存在该托管订阅"
|
||||||
|
|
||||||
msgid "Node group import failed: %s"
|
msgid "Node group import failed: %s"
|
||||||
msgstr "节点组导入失败:%s"
|
msgstr "节点组导入失败:%s"
|
||||||
|
|
||||||
|
|||||||
@@ -31,6 +31,12 @@ paths() {
|
|||||||
case "${name%.sub}" in ''|*[!A-Za-z0-9_]*) fail 'unexpected subscription filename' ;; esac
|
case "${name%.sub}" in ''|*[!A-Za-z0-9_]*) fail 'unexpected subscription filename' ;; esac
|
||||||
printf 'etc/dae/subscriptions/%s\n' "$name"
|
printf 'etc/dae/subscriptions/%s\n' "$name"
|
||||||
done
|
done
|
||||||
|
for sub in "$1"/etc/daed/daede-sub-*; do
|
||||||
|
[ -e "$sub" ] || [ -L "$sub" ] || continue
|
||||||
|
name="${sub##*/daede-sub-}"
|
||||||
|
case "$name" in ''|*[!A-Za-z0-9]*) fail 'unexpected daed subscription filename' ;; esac
|
||||||
|
printf 'etc/daed/daede-sub-%s\n' "$name"
|
||||||
|
done
|
||||||
}
|
}
|
||||||
check_paths() {
|
check_paths() {
|
||||||
for dir in /etc/config /etc/dae /etc/daed /etc/dae/subscriptions; do
|
for dir in /etc/config /etc/dae /etc/daed /etc/dae/subscriptions; do
|
||||||
@@ -42,6 +48,14 @@ check_paths() {
|
|||||||
[ ! -e "/$p" ] || [ -f "/$p" ] || fail "not a regular file: /$p"
|
[ ! -e "/$p" ] || [ -f "/$p" ] || fail "not a regular file: /$p"
|
||||||
done < "$WORK/paths"
|
done < "$WORK/paths"
|
||||||
}
|
}
|
||||||
|
cleanup_stages() {
|
||||||
|
for stage in /etc/daed/.daede-sub-stage-*; do
|
||||||
|
[ -e "$stage" ] || [ -L "$stage" ] || continue
|
||||||
|
[ ! -L "$stage" ] || fail "refusing symlink: $stage"
|
||||||
|
[ -f "$stage" ] || fail "not a regular file: $stage"
|
||||||
|
rm -f "$stage" || fail "failed to remove stale snapshot stage: $stage"
|
||||||
|
done
|
||||||
|
}
|
||||||
snapshot() {
|
snapshot() {
|
||||||
mkdir -p "$WORK/before"
|
mkdir -p "$WORK/before"
|
||||||
while IFS= read -r p; do
|
while IFS= read -r p; do
|
||||||
@@ -139,6 +153,9 @@ validate_archive() {
|
|||||||
etc/dae/subscriptions/*.sub)
|
etc/dae/subscriptions/*.sub)
|
||||||
name="${p#etc/dae/subscriptions/}"
|
name="${p#etc/dae/subscriptions/}"
|
||||||
case "${name%.sub}" in ''|*[!A-Za-z0-9_]*) fail 'invalid subscription path' ;; esac ;;
|
case "${name%.sub}" in ''|*[!A-Za-z0-9_]*) fail 'invalid subscription path' ;; esac ;;
|
||||||
|
etc/daed/daede-sub-*)
|
||||||
|
name="${p#etc/daed/daede-sub-}"
|
||||||
|
case "$name" in ''|*[!A-Za-z0-9]*) fail 'invalid daed subscription path' ;; esac ;;
|
||||||
*) fail 'unexpected archive entry' ;;
|
*) fail 'unexpected archive entry' ;;
|
||||||
esac
|
esac
|
||||||
done < "$WORK/entries"
|
done < "$WORK/entries"
|
||||||
@@ -175,6 +192,7 @@ run_action() {
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
stop_backends
|
stop_backends
|
||||||
|
[ "$ACTION" = reset ] && cleanup_stages
|
||||||
snapshot
|
snapshot
|
||||||
if [ "$ACTION" = export ]; then
|
if [ "$ACTION" = export ]; then
|
||||||
paths "$WORK/before" > "$WORK/candidates"
|
paths "$WORK/before" > "$WORK/candidates"
|
||||||
|
|||||||
@@ -0,0 +1,81 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# Publish converter snapshots without exposing a partially written final file.
|
||||||
|
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
DIR=/etc/daed
|
||||||
|
PREFIX=daede-sub
|
||||||
|
STAGE_PREFIX=.daede-sub-stage
|
||||||
|
|
||||||
|
fail() {
|
||||||
|
echo "$*" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
valid_token() {
|
||||||
|
[ "$#" -eq 1 ] || return 1
|
||||||
|
case "$1" in
|
||||||
|
''|*[!A-Za-z0-9]*) return 1 ;;
|
||||||
|
esac
|
||||||
|
[ "${#1}" -le 64 ]
|
||||||
|
}
|
||||||
|
|
||||||
|
stage_path() { printf '%s/%s-%s\n' "$DIR" "$STAGE_PREFIX" "$1"; }
|
||||||
|
final_path() { printf '%s/%s-%s\n' "$DIR" "$PREFIX" "$1"; }
|
||||||
|
|
||||||
|
regular_file() {
|
||||||
|
[ -f "$1" ] && [ ! -L "$1" ]
|
||||||
|
}
|
||||||
|
|
||||||
|
safe_dir() {
|
||||||
|
[ -d "$DIR" ] && [ ! -L "$DIR" ] || fail "invalid snapshot directory"
|
||||||
|
}
|
||||||
|
|
||||||
|
publish() {
|
||||||
|
[ "$#" -eq 1 ] || fail 'publish expects one token'
|
||||||
|
valid_token "$1" || fail 'invalid snapshot token'
|
||||||
|
safe_dir
|
||||||
|
stage=$(stage_path "$1")
|
||||||
|
final=$(final_path "$1")
|
||||||
|
regular_file "$stage" || fail 'snapshot stage is not a regular file'
|
||||||
|
[ ! -e "$final" ] && [ ! -L "$final" ] || fail 'snapshot already exists'
|
||||||
|
chmod 600 "$stage" || fail 'cannot secure snapshot stage'
|
||||||
|
mv -f "$stage" "$final" || fail 'cannot publish snapshot'
|
||||||
|
}
|
||||||
|
|
||||||
|
replace() {
|
||||||
|
[ "$#" -eq 2 ] || fail 'replace expects existing and stage tokens'
|
||||||
|
valid_token "$1" || fail 'invalid existing snapshot token'
|
||||||
|
valid_token "$2" || fail 'invalid stage snapshot token'
|
||||||
|
safe_dir
|
||||||
|
[ "$1" != "$2" ] || fail 'existing and stage tokens must differ'
|
||||||
|
stage=$(stage_path "$2")
|
||||||
|
final=$(final_path "$1")
|
||||||
|
regular_file "$stage" || fail 'snapshot stage is not a regular file'
|
||||||
|
if [ -e "$final" ] || [ -L "$final" ]; then
|
||||||
|
regular_file "$final" || fail 'existing snapshot is not a regular file'
|
||||||
|
fi
|
||||||
|
chmod 600 "$stage" || fail 'cannot secure snapshot stage'
|
||||||
|
mv -f "$stage" "$final" || fail 'cannot replace snapshot'
|
||||||
|
}
|
||||||
|
|
||||||
|
discard() {
|
||||||
|
[ "$#" -eq 1 ] || fail 'discard expects one token'
|
||||||
|
valid_token "$1" || fail 'invalid snapshot token'
|
||||||
|
safe_dir
|
||||||
|
stage=$(stage_path "$1")
|
||||||
|
if [ -L "$stage" ]; then
|
||||||
|
fail 'refusing symlink snapshot stage'
|
||||||
|
fi
|
||||||
|
if [ -e "$stage" ]; then
|
||||||
|
[ -f "$stage" ] || fail 'snapshot stage is not a regular file'
|
||||||
|
rm -f "$stage" || fail 'cannot discard snapshot stage'
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
case "${1:-}" in
|
||||||
|
publish) shift; publish "$@" ;;
|
||||||
|
replace) shift; replace "$@" ;;
|
||||||
|
discard) shift; discard "$@" ;;
|
||||||
|
*) fail 'unknown snapshot action' ;;
|
||||||
|
esac
|
||||||
@@ -43,9 +43,12 @@
|
|||||||
"/tmp/dae-validate.dae": [ "write" ],
|
"/tmp/dae-validate.dae": [ "write" ],
|
||||||
"/tmp/daede-import.b64": [ "write" ],
|
"/tmp/daede-import.b64": [ "write" ],
|
||||||
"/tmp/daede-sub.txt": [ "write" ],
|
"/tmp/daede-sub.txt": [ "write" ],
|
||||||
"/tmp/daede-daedsub-*": [ "write" ],
|
"/etc/daed/.daede-sub-stage-*": [ "write" ],
|
||||||
"/bin/rm -f /tmp/daede-daedsub-*": [ "exec" ],
|
"/etc/daed/daede-sub-*": [ "write" ],
|
||||||
"/usr/share/luci-app-daede/config-backup.sh import": [ "exec" ],
|
"/usr/share/luci-app-daede/config-backup.sh import": [ "exec" ],
|
||||||
|
"/usr/share/luci-app-daede/snapshot-file.sh publish *": [ "exec" ],
|
||||||
|
"/usr/share/luci-app-daede/snapshot-file.sh replace * *": [ "exec" ],
|
||||||
|
"/usr/share/luci-app-daede/snapshot-file.sh discard *": [ "exec" ],
|
||||||
"/usr/share/luci-app-daede/gen-dae-config.sh write-sub *": [ "exec" ],
|
"/usr/share/luci-app-daede/gen-dae-config.sh write-sub *": [ "exec" ],
|
||||||
"/usr/share/luci-app-daede/gen-dae-config.sh delete-sub *": [ "exec" ],
|
"/usr/share/luci-app-daede/gen-dae-config.sh delete-sub *": [ "exec" ],
|
||||||
"/etc/config/daede": [ "write" ],
|
"/etc/config/daede": [ "write" ],
|
||||||
|
|||||||
@@ -2,13 +2,13 @@
|
|||||||
# daede-sub - serve a converter's staged node list to the local daed only.
|
# daede-sub - serve a converter's staged node list to the local daed only.
|
||||||
#
|
#
|
||||||
# daed (dae-wing) can only build a subscription by HTTP-fetching a link, and it
|
# daed (dae-wing) can only build a subscription by HTTP-fetching a link, and it
|
||||||
# does not read file:// like dae core. So the converter stages the base64 share
|
# does not read file:// like dae core. So the converter stores the base64 share
|
||||||
# links in /tmp/daede-daedsub-<token> and points daed's importSubscription at
|
# links in /etc/daed/daede-sub-<token> and points daed's importSubscription at
|
||||||
# http://127.0.0.1/cgi-bin/daede-sub?t=<token>. This CGI is the read side.
|
# http://127.0.0.1/cgi-bin/daede-sub?t=<token>. This CGI is the read side.
|
||||||
#
|
#
|
||||||
# Locked to loopback (REMOTE_ADDR), so even though uhttpd listens on the LAN no
|
# Locked to loopback (REMOTE_ADDR), so even though uhttpd listens on the LAN no
|
||||||
# remote client can pull the links. The frontend deletes the staged file after
|
# remote client can pull the links. The snapshot remains available for daed's
|
||||||
# the import (success or failure), so the exposure is one local fetch.
|
# own refresh action and is included in daede configuration backups.
|
||||||
|
|
||||||
deny() { printf 'Status: 403 Forbidden\r\nContent-Type: text/plain\r\n\r\nforbidden\n'; exit 0; }
|
deny() { printf 'Status: 403 Forbidden\r\nContent-Type: text/plain\r\n\r\nforbidden\n'; exit 0; }
|
||||||
|
|
||||||
@@ -18,7 +18,7 @@ deny() { printf 'Status: 403 Forbidden\r\nContent-Type: text/plain\r\n\r\nforbid
|
|||||||
t=$(printf '%s' "$QUERY_STRING" | sed -n 's/^.*\bt=\([A-Za-z0-9]\{1,64\}\).*$/\1/p')
|
t=$(printf '%s' "$QUERY_STRING" | sed -n 's/^.*\bt=\([A-Za-z0-9]\{1,64\}\).*$/\1/p')
|
||||||
[ -n "$t" ] || deny
|
[ -n "$t" ] || deny
|
||||||
|
|
||||||
f="/tmp/daede-daedsub-$t"
|
f="/etc/daed/daede-sub-$t"
|
||||||
[ -f "$f" ] || deny
|
[ -f "$f" ] || deny
|
||||||
|
|
||||||
printf 'Content-Type: text/plain\r\n\r\n'
|
printf 'Content-Type: text/plain\r\n\r\n'
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
},
|
},
|
||||||
xray: {
|
xray: {
|
||||||
label: '<%:Xray-core%>',
|
label: '<%:Xray-core%>',
|
||||||
help: '<%:The online upgrade downloads the matching Xray-core linux archive for the current ARCH from the official GitHub release page.%>'
|
help: '<%:The online upgrade downloads and installs the matching Xray-core package for this system from fw876/helloworld releases.%>'
|
||||||
},
|
},
|
||||||
mihomo: {
|
mihomo: {
|
||||||
label: '<%:Mihomo%>',
|
label: '<%:Mihomo%>',
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
},
|
},
|
||||||
naiveproxy: {
|
naiveproxy: {
|
||||||
label: '<%:NaiveProxy%>',
|
label: '<%:NaiveProxy%>',
|
||||||
help: '<%:The online upgrade tracks the latest NaiveProxy release and prefers OpenWrt static archives matching the current ARCH before falling back to other release assets.%>'
|
help: '<%:The online upgrade downloads and installs the matching NaiveProxy package for this system from fw876/helloworld releases.%>'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -2667,8 +2667,8 @@ msgstr ""
|
|||||||
|
|
||||||
#: applications/luci-app-ssr-plus/luasrc/view/shadowsocksr/component.htm:10
|
#: applications/luci-app-ssr-plus/luasrc/view/shadowsocksr/component.htm:10
|
||||||
msgid ""
|
msgid ""
|
||||||
"The online upgrade downloads the matching Xray-core linux archive for the "
|
"The online upgrade downloads and installs the matching Xray-core package for "
|
||||||
"current ARCH from the official GitHub release page."
|
"this system from fw876/helloworld releases."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: applications/luci-app-ssr-plus/luasrc/view/shadowsocksr/component.htm:14
|
#: applications/luci-app-ssr-plus/luasrc/view/shadowsocksr/component.htm:14
|
||||||
@@ -2680,9 +2680,8 @@ msgstr ""
|
|||||||
|
|
||||||
#: applications/luci-app-ssr-plus/luasrc/view/shadowsocksr/component.htm:18
|
#: applications/luci-app-ssr-plus/luasrc/view/shadowsocksr/component.htm:18
|
||||||
msgid ""
|
msgid ""
|
||||||
"The online upgrade tracks the latest NaiveProxy release and prefers OpenWrt "
|
"The online upgrade downloads and installs the matching NaiveProxy package "
|
||||||
"static archives matching the current ARCH before falling back to other "
|
"for this system from fw876/helloworld releases."
|
||||||
"release assets."
|
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: applications/luci-app-ssr-plus/luasrc/view/shadowsocksr/clash_main_panel.htm:816
|
#: applications/luci-app-ssr-plus/luasrc/view/shadowsocksr/clash_main_panel.htm:816
|
||||||
|
|||||||
@@ -2689,11 +2689,10 @@ msgstr ""
|
|||||||
|
|
||||||
#: applications/luci-app-ssr-plus/luasrc/view/shadowsocksr/component.htm:10
|
#: applications/luci-app-ssr-plus/luasrc/view/shadowsocksr/component.htm:10
|
||||||
msgid ""
|
msgid ""
|
||||||
"The online upgrade downloads the matching Xray-core linux archive for the "
|
"The online upgrade downloads and installs the matching Xray-core package for "
|
||||||
"current ARCH from the official GitHub release page."
|
"this system from fw876/helloworld releases."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"在线升级会根据当前架构,从 Xray-core 官方 GitHub 发布页下载匹配的 Linux 压缩"
|
"在线升级会从 fw876/helloworld 发布页下载并安装适配当前系统的 Xray-core 软件包。"
|
||||||
"包。"
|
|
||||||
|
|
||||||
#: applications/luci-app-ssr-plus/luasrc/view/shadowsocksr/component.htm:14
|
#: applications/luci-app-ssr-plus/luasrc/view/shadowsocksr/component.htm:14
|
||||||
msgid ""
|
msgid ""
|
||||||
@@ -2706,12 +2705,10 @@ msgstr ""
|
|||||||
|
|
||||||
#: applications/luci-app-ssr-plus/luasrc/view/shadowsocksr/component.htm:18
|
#: applications/luci-app-ssr-plus/luasrc/view/shadowsocksr/component.htm:18
|
||||||
msgid ""
|
msgid ""
|
||||||
"The online upgrade tracks the latest NaiveProxy release and prefers OpenWrt "
|
"The online upgrade downloads and installs the matching NaiveProxy package "
|
||||||
"static archives matching the current ARCH before falling back to other "
|
"for this system from fw876/helloworld releases."
|
||||||
"release assets."
|
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"在线升级会跟踪最新 NaiveProxy 发布版,并优先选择与当前架构匹配的 OpenWrt "
|
"在线升级会从 fw876/helloworld 发布页下载并安装适配当前系统的 NaiveProxy 软件包。"
|
||||||
"static 压缩包,找不到时再回退到其他发布资产。"
|
|
||||||
|
|
||||||
#: applications/luci-app-ssr-plus/luasrc/view/shadowsocksr/clash_main_panel.htm:816
|
#: applications/luci-app-ssr-plus/luasrc/view/shadowsocksr/clash_main_panel.htm:816
|
||||||
#: applications/luci-app-ssr-plus/luasrc/view/shadowsocksr/clash_main_panel.htm:871
|
#: applications/luci-app-ssr-plus/luasrc/view/shadowsocksr/clash_main_panel.htm:871
|
||||||
|
|||||||
@@ -2,13 +2,11 @@
|
|||||||
|
|
||||||
set -u
|
set -u
|
||||||
|
|
||||||
XRAY_RELEASE_PAGE="https://github.com/XTLS/Xray-core/releases/latest"
|
XRAY_RELEASE_PAGE="https://github.com/fw876/helloworld/releases/latest"
|
||||||
MIHOMO_RELEASE_PAGE="https://github.com/MetaCubeX/mihomo/releases/latest"
|
MIHOMO_RELEASE_PAGE="https://github.com/MetaCubeX/mihomo/releases/latest"
|
||||||
NAIVEPROXY_RELEASE_API="https://api.github.com/repos/klzgrad/naiveproxy/releases/latest"
|
|
||||||
HELLOWORLD_RELEASE_API="https://api.github.com/repos/fw876/helloworld/releases/latest"
|
HELLOWORLD_RELEASE_API="https://api.github.com/repos/fw876/helloworld/releases/latest"
|
||||||
XRAY_BINARY="/usr/bin/xray"
|
XRAY_BINARY="/usr/bin/xray"
|
||||||
MIHOMO_BINARY="/usr/bin/mihomo"
|
MIHOMO_BINARY="/usr/bin/mihomo"
|
||||||
NAIVEPROXY_BINARY="/usr/bin/naive"
|
|
||||||
|
|
||||||
# Geo 数据文件 URL
|
# Geo 数据文件 URL
|
||||||
GEOIP_REPO="Loyalsoldier/geoip"
|
GEOIP_REPO="Loyalsoldier/geoip"
|
||||||
@@ -71,9 +69,6 @@ mirror_wrap_url() {
|
|||||||
;;
|
;;
|
||||||
jsdelivr)
|
jsdelivr)
|
||||||
case "$raw_url" in
|
case "$raw_url" in
|
||||||
https://github.com/XTLS/Xray-core/releases/download/*)
|
|
||||||
printf '%s' "$raw_url" | sed 's#https://github.com/XTLS/Xray-core/releases/download/\(v[^/]*\)/\(.*\)#https://fastly.jsdelivr.net/gh/XTLS/Xray-core@\1/\2#'
|
|
||||||
;;
|
|
||||||
https://github.com/MetaCubeX/mihomo/releases/download/*)
|
https://github.com/MetaCubeX/mihomo/releases/download/*)
|
||||||
printf '%s' "$raw_url" | sed 's#https://github.com/MetaCubeX/mihomo/releases/download/\(v[^/]*\)/\(.*\)#https://fastly.jsdelivr.net/gh/MetaCubeX/mihomo@\1/\2#'
|
printf '%s' "$raw_url" | sed 's#https://github.com/MetaCubeX/mihomo/releases/download/\(v[^/]*\)/\(.*\)#https://fastly.jsdelivr.net/gh/MetaCubeX/mihomo@\1/\2#'
|
||||||
;;
|
;;
|
||||||
@@ -141,10 +136,6 @@ get_openwrt_arch() {
|
|||||||
printf '%s' "$arch"
|
printf '%s' "$arch"
|
||||||
}
|
}
|
||||||
|
|
||||||
is_openwrt_env() {
|
|
||||||
[ -r /etc/openwrt_release ] || command -v opkg >/dev/null 2>&1
|
|
||||||
}
|
|
||||||
|
|
||||||
detect_package_manager() {
|
detect_package_manager() {
|
||||||
if command -v apk >/dev/null 2>&1; then
|
if command -v apk >/dev/null 2>&1; then
|
||||||
printf '%s' 'apk'
|
printf '%s' 'apk'
|
||||||
@@ -209,81 +200,32 @@ get_mihomo_current_version() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get_naiveproxy_current_version() {
|
get_naiveproxy_current_version() {
|
||||||
local binary
|
local pm version binary
|
||||||
|
|
||||||
|
pm="$(detect_package_manager 2>/dev/null || true)"
|
||||||
|
case "$pm" in
|
||||||
|
apk)
|
||||||
|
version="$(apk list -I naiveproxy 2>/dev/null | sed -n 's/^naiveproxy-\([^[:space:]]*\).*$/\1/p' | sed -n '1p')"
|
||||||
|
;;
|
||||||
|
opkg)
|
||||||
|
version="$(opkg status naiveproxy 2>/dev/null | sed -n 's/^Version:[[:space:]]*//p' | sed -n '1p')"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if [ -n "${version:-}" ]; then
|
||||||
|
printf '%s' "$version"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
binary="$(find_naiveproxy_binary)" || return 1
|
binary="$(find_naiveproxy_binary)" || return 1
|
||||||
"$binary" --version 2>&1 | grep -Eo '[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+(-[0-9]+)?' | sed -n '1p'
|
"$binary" --version 2>&1 | grep -Eo '[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+(-[0-9]+)?' | sed -n '1p'
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
map_xray_asset() {
|
|
||||||
case "$1" in
|
|
||||||
x86_64*|amd64*)
|
|
||||||
printf '%s' 'Xray-linux-64.zip'
|
|
||||||
;;
|
|
||||||
i386*|i486*|i586*|i686*|x86*)
|
|
||||||
printf '%s' 'Xray-linux-32.zip'
|
|
||||||
;;
|
|
||||||
aarch64*|arm64*)
|
|
||||||
printf '%s' 'Xray-linux-arm64-v8a.zip'
|
|
||||||
;;
|
|
||||||
*armv7*|*cortex-a*|*neon*|*vfpv3*|*vfpv4*)
|
|
||||||
printf '%s' 'Xray-linux-arm32-v7a.zip'
|
|
||||||
;;
|
|
||||||
*armv6*|*arm1176*)
|
|
||||||
printf '%s' 'Xray-linux-arm32-v6.zip'
|
|
||||||
;;
|
|
||||||
*armv5*|*arm926*|*xscale*)
|
|
||||||
printf '%s' 'Xray-linux-arm32-v5.zip'
|
|
||||||
;;
|
|
||||||
mips64el*|mips64le*)
|
|
||||||
printf '%s' 'Xray-linux-mips64le.zip'
|
|
||||||
;;
|
|
||||||
mips64*)
|
|
||||||
printf '%s' 'Xray-linux-mips64.zip'
|
|
||||||
;;
|
|
||||||
mipsel*|mips32el*|mips32le*)
|
|
||||||
printf '%s' 'Xray-linux-mips32le.zip'
|
|
||||||
;;
|
|
||||||
mips*)
|
|
||||||
printf '%s' 'Xray-linux-mips32.zip'
|
|
||||||
;;
|
|
||||||
riscv64*)
|
|
||||||
printf '%s' 'Xray-linux-riscv64.zip'
|
|
||||||
;;
|
|
||||||
loongarch64*|loong64*)
|
|
||||||
printf '%s' 'Xray-linux-loong64.zip'
|
|
||||||
;;
|
|
||||||
powerpc64*|ppc64*)
|
|
||||||
printf '%s' 'Xray-linux-ppc64.zip'
|
|
||||||
;;
|
|
||||||
s390x*)
|
|
||||||
printf '%s' 'Xray-linux-s390x.zip'
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
return 1
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
}
|
|
||||||
|
|
||||||
require_cmd() {
|
require_cmd() {
|
||||||
command -v "$1" >/dev/null 2>&1
|
command -v "$1" >/dev/null 2>&1
|
||||||
}
|
}
|
||||||
|
|
||||||
select_unzip_cmd() {
|
|
||||||
if require_cmd unzip; then
|
|
||||||
printf '%s' 'unzip -oq'
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
if busybox unzip >/dev/null 2>&1; then
|
|
||||||
printf '%s' 'busybox unzip -o'
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
select_gzip_cmd() {
|
select_gzip_cmd() {
|
||||||
if require_cmd gzip; then
|
if require_cmd gzip; then
|
||||||
printf '%s' 'gzip -dc'
|
printf '%s' 'gzip -dc'
|
||||||
@@ -298,34 +240,6 @@ select_gzip_cmd() {
|
|||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
select_xz_cmd() {
|
|
||||||
if require_cmd xz; then
|
|
||||||
printf '%s' 'xz -dc'
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
if busybox xz >/dev/null 2>&1; then
|
|
||||||
printf '%s' 'busybox xz -dc'
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
select_tar_cmd() {
|
|
||||||
if require_cmd tar; then
|
|
||||||
printf '%s' 'tar'
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
if busybox tar --help >/dev/null 2>&1; then
|
|
||||||
printf '%s' 'busybox tar'
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
select_wget_cmd() {
|
select_wget_cmd() {
|
||||||
if require_cmd wget-ssl; then
|
if require_cmd wget-ssl; then
|
||||||
printf '%s' 'wget-ssl'
|
printf '%s' 'wget-ssl'
|
||||||
@@ -1080,22 +994,72 @@ get_xray_latest_tag() {
|
|||||||
local location tag
|
local location tag
|
||||||
|
|
||||||
location="$(effective_url "$XRAY_RELEASE_PAGE")" || return 1
|
location="$(effective_url "$XRAY_RELEASE_PAGE")" || return 1
|
||||||
tag="$(printf '%s' "$location" | sed -n 's#.*/tag/\(v[0-9][^/]*\)$#\1#p' | sed -n '1p')"
|
tag="$(printf '%s' "$location" | sed -n 's#.*/tag/\([^/]*\)$#\1#p' | sed -n '1p')"
|
||||||
[ -n "$tag" ] || return 1
|
[ -n "$tag" ] || return 1
|
||||||
printf '%s' "$tag"
|
printf '%s' "$tag"
|
||||||
}
|
}
|
||||||
|
|
||||||
get_xray_latest_info() {
|
select_xray_asset() {
|
||||||
local tag version asset url arch
|
local asset_list="$1"
|
||||||
|
local pm="$2"
|
||||||
|
local arch="$3"
|
||||||
|
local candidate
|
||||||
|
|
||||||
|
case "$pm" in
|
||||||
|
apk)
|
||||||
|
candidate="$(printf '%s\n' "$asset_list" | grep -E '^xray-core-[^[:space:]]+\.apk$' | grep -F "_${arch}.apk" | sed -n '1p')"
|
||||||
|
;;
|
||||||
|
opkg)
|
||||||
|
candidate="$(printf '%s\n' "$asset_list" | grep -E '^xray-core_[^[:space:]]+\.ipk$' | grep -F "_${arch}.ipk" | sed -n '1p')"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
return 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
[ -n "$candidate" ] || return 1
|
||||||
|
printf '%s' "$candidate"
|
||||||
|
}
|
||||||
|
|
||||||
|
xray_asset_version() {
|
||||||
|
local asset="$1"
|
||||||
|
local arch="$2"
|
||||||
|
local version
|
||||||
|
|
||||||
|
case "$asset" in
|
||||||
|
xray-core_*_"$arch".ipk)
|
||||||
|
version="${asset#xray-core_}"
|
||||||
|
version="${version%_${arch}.ipk}"
|
||||||
|
;;
|
||||||
|
xray-core-*_"$arch".apk)
|
||||||
|
version="${asset#xray-core-}"
|
||||||
|
version="${version%_${arch}.apk}"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
return 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
version="$(printf '%s' "$version" | sed 's/-r[0-9][0-9]*$//')"
|
||||||
|
[ -n "$version" ] || return 1
|
||||||
|
printf '%s' "$version"
|
||||||
|
}
|
||||||
|
|
||||||
|
get_xray_latest_info() {
|
||||||
|
local pm tag version asset asset_list release_html url arch
|
||||||
|
|
||||||
|
pm="$(detect_package_manager)" || return 2
|
||||||
arch="$(get_openwrt_arch)"
|
arch="$(get_openwrt_arch)"
|
||||||
asset="$(map_xray_asset "$arch")" || return 2
|
|
||||||
tag="$(get_xray_latest_tag)" || return 3
|
tag="$(get_xray_latest_tag)" || return 3
|
||||||
version="$(trim_version "$tag")"
|
release_html="$(fetch_text "https://github.com/fw876/helloworld/releases/expanded_assets/$tag")" || return 3
|
||||||
url="$(mirror_wrap_url "https://github.com/XTLS/Xray-core/releases/download/$tag/$asset")"
|
asset_list="$(printf '%s\n' "$release_html" | sed -n 's#.*href="/fw876/helloworld/releases/download/[^/]*/\([^"]*\)".*#\1#p')"
|
||||||
|
asset="$(select_xray_asset "$asset_list" "$pm" "$arch")" || return 4
|
||||||
|
version="$(xray_asset_version "$asset" "$arch")" || return 4
|
||||||
|
url="$(mirror_wrap_url "https://github.com/fw876/helloworld/releases/download/$tag/$asset")"
|
||||||
|
|
||||||
[ -n "$tag" ] && [ -n "$version" ] && [ -n "$url" ] || return 4
|
[ -n "$tag" ] && [ -n "$version" ] && [ -n "$url" ] || return 4
|
||||||
|
|
||||||
|
log_kv package_manager "$pm"
|
||||||
log_kv arch "$arch"
|
log_kv arch "$arch"
|
||||||
log_kv asset "$asset"
|
log_kv asset "$asset"
|
||||||
log_kv latest_version "$version"
|
log_kv latest_version "$version"
|
||||||
@@ -1181,112 +1145,64 @@ get_mihomo_latest_info() {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
map_naiveproxy_linux_asset() {
|
select_naiveproxy_asset() {
|
||||||
case "$1" in
|
local asset_list="$1"
|
||||||
x86_64*|amd64*)
|
local pm="$2"
|
||||||
printf '%s' 'x64'
|
local arch="$3"
|
||||||
|
local candidate
|
||||||
|
|
||||||
|
case "$pm" in
|
||||||
|
apk)
|
||||||
|
candidate="$(printf '%s\n' "$asset_list" | grep -E '^naiveproxy-[^[:space:]]+\.apk$' | grep -F "_${arch}.apk" | sed -n '1p')"
|
||||||
;;
|
;;
|
||||||
i386*|i486*|i586*|i686*|x86*)
|
opkg)
|
||||||
printf '%s' 'x86'
|
candidate="$(printf '%s\n' "$asset_list" | grep -E '^naiveproxy_[^[:space:]]+\.ipk$' | grep -F "_${arch}.ipk" | sed -n '1p')"
|
||||||
;;
|
|
||||||
aarch64*|arm64*)
|
|
||||||
printf '%s' 'arm64'
|
|
||||||
;;
|
|
||||||
*armv7*|*cortex-a*|*neon*|*vfpv3*|*vfpv4*)
|
|
||||||
printf '%s' 'arm'
|
|
||||||
;;
|
|
||||||
mips64el*|mips64le*)
|
|
||||||
printf '%s' 'mips64el'
|
|
||||||
;;
|
|
||||||
mipsel*|mips32el*|mips32le*)
|
|
||||||
printf '%s' 'mipsel'
|
|
||||||
;;
|
|
||||||
riscv64*)
|
|
||||||
printf '%s' 'riscv64'
|
|
||||||
;;
|
|
||||||
loongarch64*|loong64*)
|
|
||||||
printf '%s' 'loong64'
|
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
return 1
|
return 1
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
|
[ -n "$candidate" ] || return 1
|
||||||
|
printf '%s' "$candidate"
|
||||||
}
|
}
|
||||||
|
|
||||||
normalize_naiveproxy_openwrt_arch() {
|
naiveproxy_asset_version() {
|
||||||
local arch="${1%%+*}"
|
local asset="$1"
|
||||||
printf '%s' "$arch"
|
local arch="$2"
|
||||||
}
|
local version
|
||||||
|
|
||||||
naiveproxy_openwrt_arch_candidates() {
|
case "$asset" in
|
||||||
local normalized
|
naiveproxy_*_"$arch".ipk)
|
||||||
|
version="${asset#naiveproxy_}"
|
||||||
normalized="$(normalize_naiveproxy_openwrt_arch "$1")"
|
version="${version%_${arch}.ipk}"
|
||||||
printf '%s\n' "$normalized"
|
|
||||||
|
|
||||||
case "$normalized" in
|
|
||||||
aarch64_*)
|
|
||||||
[ "$normalized" = "aarch64_generic" ] || printf '%s\n' 'aarch64_generic'
|
|
||||||
;;
|
;;
|
||||||
x86|i386|i486|i586|i686)
|
naiveproxy-*_"$arch".apk)
|
||||||
printf '%s\n' 'x86'
|
version="${asset#naiveproxy-}"
|
||||||
|
version="${version%_${arch}.apk}"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
return 1
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
}
|
|
||||||
|
|
||||||
asset_list_has() {
|
[ -n "$version" ] || return 1
|
||||||
local asset_list="$1"
|
printf '%s' "$version"
|
||||||
local candidate="$2"
|
|
||||||
|
|
||||||
printf '%s\n' "$asset_list" | grep -Fx "$candidate" >/dev/null 2>&1
|
|
||||||
}
|
|
||||||
|
|
||||||
select_naiveproxy_asset() {
|
|
||||||
local asset_list="$1"
|
|
||||||
local tag="$2"
|
|
||||||
local arch="$3"
|
|
||||||
local candidate linux_arch candidate_arch
|
|
||||||
|
|
||||||
if is_openwrt_env; then
|
|
||||||
for candidate_arch in $(naiveproxy_openwrt_arch_candidates "$arch"); do
|
|
||||||
candidate="naiveproxy-${tag}-openwrt-${candidate_arch}-static.tar.xz"
|
|
||||||
if asset_list_has "$asset_list" "$candidate"; then
|
|
||||||
printf '%s' "$candidate"
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
candidate="naiveproxy-${tag}-openwrt-${candidate_arch}.tar.xz"
|
|
||||||
if asset_list_has "$asset_list" "$candidate"; then
|
|
||||||
printf '%s' "$candidate"
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
fi
|
|
||||||
|
|
||||||
linux_arch="$(map_naiveproxy_linux_asset "$arch" 2>/dev/null || true)"
|
|
||||||
if [ -n "$linux_arch" ]; then
|
|
||||||
candidate="naiveproxy-${tag}-linux-${linux_arch}.tar.xz"
|
|
||||||
if asset_list_has "$asset_list" "$candidate"; then
|
|
||||||
printf '%s' "$candidate"
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
return 1
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get_naiveproxy_latest_info() {
|
get_naiveproxy_latest_info() {
|
||||||
local arch release_json tag version asset asset_list url
|
local pm arch tag version asset asset_list release_html url
|
||||||
|
|
||||||
|
pm="$(detect_package_manager)" || return 2
|
||||||
arch="$(get_openwrt_arch)"
|
arch="$(get_openwrt_arch)"
|
||||||
release_json="$(fetch_text "$NAIVEPROXY_RELEASE_API")" || return 3
|
tag="$(get_xray_latest_tag)" || return 3
|
||||||
tag="$(printf '%s\n' "$release_json" | sed -n 's/.*"tag_name":[[:space:]]*"\([^"]*\)".*/\1/p' | sed -n '1p')"
|
release_html="$(fetch_text "https://github.com/fw876/helloworld/releases/expanded_assets/$tag")" || return 3
|
||||||
[ -n "$tag" ] || return 3
|
asset_list="$(printf '%s\n' "$release_html" | sed -n 's#.*href="/fw876/helloworld/releases/download/[^/]*/\([^"]*\)".*#\1#p')"
|
||||||
version="$(trim_version "$tag")"
|
asset="$(select_naiveproxy_asset "$asset_list" "$pm" "$arch")" || return 4
|
||||||
asset_list="$(printf '%s\n' "$release_json" | sed -n 's/.*"name":[[:space:]]*"\([^"]*\.tar\.xz\)".*/\1/p')"
|
version="$(naiveproxy_asset_version "$asset" "$arch")" || return 4
|
||||||
asset="$(select_naiveproxy_asset "$asset_list" "$tag" "$arch")" || return 4
|
url="$(mirror_wrap_url "https://github.com/fw876/helloworld/releases/download/$tag/$asset")"
|
||||||
url="$(mirror_wrap_url "https://github.com/klzgrad/naiveproxy/releases/download/$tag/$asset")"
|
|
||||||
|
|
||||||
|
log_kv package_manager "$pm"
|
||||||
log_kv arch "$arch"
|
log_kv arch "$arch"
|
||||||
log_kv asset "$asset"
|
log_kv asset "$asset"
|
||||||
log_kv latest_version "$version"
|
log_kv latest_version "$version"
|
||||||
@@ -1529,12 +1445,13 @@ mainprogram_upgrade() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
xray_info() {
|
xray_info() {
|
||||||
local current installed latest_output latest_rc latest_version arch asset can_upgrade
|
local pm current installed latest_output latest_rc latest_version arch asset can_upgrade
|
||||||
|
|
||||||
installed=0
|
installed=0
|
||||||
current=""
|
current=""
|
||||||
|
pm="$(detect_package_manager 2>/dev/null || true)"
|
||||||
arch="$(get_openwrt_arch)"
|
arch="$(get_openwrt_arch)"
|
||||||
asset="$(map_xray_asset "$arch" 2>/dev/null || true)"
|
asset=""
|
||||||
if current="$(get_xray_current_version)" && [ -n "$current" ]; then
|
if current="$(get_xray_current_version)" && [ -n "$current" ]; then
|
||||||
installed=1
|
installed=1
|
||||||
fi
|
fi
|
||||||
@@ -1545,13 +1462,14 @@ xray_info() {
|
|||||||
log_kv component xray
|
log_kv component xray
|
||||||
log_kv installed "$installed"
|
log_kv installed "$installed"
|
||||||
log_kv current_version "$current"
|
log_kv current_version "$current"
|
||||||
|
log_kv package_manager "$pm"
|
||||||
log_kv arch "$arch"
|
log_kv arch "$arch"
|
||||||
log_kv asset "$asset"
|
log_kv asset "$asset"
|
||||||
|
|
||||||
if [ $latest_rc -ne 0 ]; then
|
if [ $latest_rc -ne 0 ]; then
|
||||||
log_kv can_upgrade 0
|
log_kv can_upgrade 0
|
||||||
case "$latest_rc" in
|
case "$latest_rc" in
|
||||||
2) log_kv error 'unsupported_arch' ;;
|
2) log_kv error 'unsupported_package_manager' ;;
|
||||||
3) log_kv error 'fetch_failed' ;;
|
3) log_kv error 'fetch_failed' ;;
|
||||||
4) log_kv error 'asset_not_found' ;;
|
4) log_kv error 'asset_not_found' ;;
|
||||||
*) log_kv error 'unknown_error' ;;
|
*) log_kv error 'unknown_error' ;;
|
||||||
@@ -1560,6 +1478,7 @@ xray_info() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
latest_version="$(printf '%s\n' "$latest_output" | sed -n 's/^latest_version=//p' | sed -n '1p')"
|
latest_version="$(printf '%s\n' "$latest_output" | sed -n 's/^latest_version=//p' | sed -n '1p')"
|
||||||
|
pm="$(printf '%s\n' "$latest_output" | sed -n 's/^package_manager=//p' | sed -n '1p')"
|
||||||
arch="$(printf '%s\n' "$latest_output" | sed -n 's/^arch=//p' | sed -n '1p')"
|
arch="$(printf '%s\n' "$latest_output" | sed -n 's/^arch=//p' | sed -n '1p')"
|
||||||
asset="$(printf '%s\n' "$latest_output" | sed -n 's/^asset=//p' | sed -n '1p')"
|
asset="$(printf '%s\n' "$latest_output" | sed -n 's/^asset=//p' | sed -n '1p')"
|
||||||
can_upgrade=0
|
can_upgrade=0
|
||||||
@@ -1619,10 +1538,11 @@ mihomo_info() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
naiveproxy_info() {
|
naiveproxy_info() {
|
||||||
local current installed latest_output latest_rc latest_version arch asset can_upgrade
|
local pm current installed latest_output latest_rc latest_version arch asset can_upgrade
|
||||||
|
|
||||||
installed=0
|
installed=0
|
||||||
current=""
|
current=""
|
||||||
|
pm="$(detect_package_manager 2>/dev/null || true)"
|
||||||
arch="$(get_openwrt_arch)"
|
arch="$(get_openwrt_arch)"
|
||||||
if current="$(get_naiveproxy_current_version)" && [ -n "$current" ]; then
|
if current="$(get_naiveproxy_current_version)" && [ -n "$current" ]; then
|
||||||
installed=1
|
installed=1
|
||||||
@@ -1634,12 +1554,14 @@ naiveproxy_info() {
|
|||||||
log_kv component naiveproxy
|
log_kv component naiveproxy
|
||||||
log_kv installed "$installed"
|
log_kv installed "$installed"
|
||||||
log_kv current_version "$current"
|
log_kv current_version "$current"
|
||||||
|
log_kv package_manager "$pm"
|
||||||
log_kv arch "$arch"
|
log_kv arch "$arch"
|
||||||
log_kv asset ''
|
log_kv asset ''
|
||||||
|
|
||||||
if [ $latest_rc -ne 0 ]; then
|
if [ $latest_rc -ne 0 ]; then
|
||||||
log_kv can_upgrade 0
|
log_kv can_upgrade 0
|
||||||
case "$latest_rc" in
|
case "$latest_rc" in
|
||||||
|
2) log_kv error 'unsupported_package_manager' ;;
|
||||||
3) log_kv error 'fetch_failed' ;;
|
3) log_kv error 'fetch_failed' ;;
|
||||||
4) log_kv error 'asset_not_found' ;;
|
4) log_kv error 'asset_not_found' ;;
|
||||||
*) log_kv error 'unknown_error' ;;
|
*) log_kv error 'unknown_error' ;;
|
||||||
@@ -1648,6 +1570,7 @@ naiveproxy_info() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
latest_version="$(printf '%s\n' "$latest_output" | sed -n 's/^latest_version=//p' | sed -n '1p')"
|
latest_version="$(printf '%s\n' "$latest_output" | sed -n 's/^latest_version=//p' | sed -n '1p')"
|
||||||
|
pm="$(printf '%s\n' "$latest_output" | sed -n 's/^package_manager=//p' | sed -n '1p')"
|
||||||
arch="$(printf '%s\n' "$latest_output" | sed -n 's/^arch=//p' | sed -n '1p')"
|
arch="$(printf '%s\n' "$latest_output" | sed -n 's/^arch=//p' | sed -n '1p')"
|
||||||
asset="$(printf '%s\n' "$latest_output" | sed -n 's/^asset=//p' | sed -n '1p')"
|
asset="$(printf '%s\n' "$latest_output" | sed -n 's/^asset=//p' | sed -n '1p')"
|
||||||
can_upgrade=0
|
can_upgrade=0
|
||||||
@@ -1663,12 +1586,12 @@ naiveproxy_info() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
xray_local_info() {
|
xray_local_info() {
|
||||||
local current installed arch asset
|
local pm current installed arch
|
||||||
|
|
||||||
installed=0
|
installed=0
|
||||||
current=""
|
current=""
|
||||||
|
pm="$(detect_package_manager 2>/dev/null || true)"
|
||||||
arch="$(get_openwrt_arch)"
|
arch="$(get_openwrt_arch)"
|
||||||
asset="$(map_xray_asset "$arch" 2>/dev/null || true)"
|
|
||||||
if current="$(get_xray_current_version)" && [ -n "$current" ]; then
|
if current="$(get_xray_current_version)" && [ -n "$current" ]; then
|
||||||
installed=1
|
installed=1
|
||||||
fi
|
fi
|
||||||
@@ -1677,8 +1600,9 @@ xray_local_info() {
|
|||||||
log_kv installed "$installed"
|
log_kv installed "$installed"
|
||||||
log_kv current_version "$current"
|
log_kv current_version "$current"
|
||||||
log_kv latest_version ''
|
log_kv latest_version ''
|
||||||
|
log_kv package_manager "$pm"
|
||||||
log_kv arch "$arch"
|
log_kv arch "$arch"
|
||||||
log_kv asset "$asset"
|
log_kv asset ''
|
||||||
log_kv can_upgrade 0
|
log_kv can_upgrade 0
|
||||||
log_kv error ''
|
log_kv error ''
|
||||||
}
|
}
|
||||||
@@ -1707,10 +1631,11 @@ mihomo_local_info() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
naiveproxy_local_info() {
|
naiveproxy_local_info() {
|
||||||
local current installed arch
|
local pm current installed arch
|
||||||
|
|
||||||
installed=0
|
installed=0
|
||||||
current=""
|
current=""
|
||||||
|
pm="$(detect_package_manager 2>/dev/null || true)"
|
||||||
arch="$(get_openwrt_arch)"
|
arch="$(get_openwrt_arch)"
|
||||||
if current="$(get_naiveproxy_current_version)" && [ -n "$current" ]; then
|
if current="$(get_naiveproxy_current_version)" && [ -n "$current" ]; then
|
||||||
installed=1
|
installed=1
|
||||||
@@ -1720,6 +1645,7 @@ naiveproxy_local_info() {
|
|||||||
log_kv installed "$installed"
|
log_kv installed "$installed"
|
||||||
log_kv current_version "$current"
|
log_kv current_version "$current"
|
||||||
log_kv latest_version ''
|
log_kv latest_version ''
|
||||||
|
log_kv package_manager "$pm"
|
||||||
log_kv arch "$arch"
|
log_kv arch "$arch"
|
||||||
log_kv asset ''
|
log_kv asset ''
|
||||||
log_kv can_upgrade 0
|
log_kv can_upgrade 0
|
||||||
@@ -1727,14 +1653,14 @@ naiveproxy_local_info() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
xray_upgrade() {
|
xray_upgrade() {
|
||||||
local latest_output latest_rc latest_version download_url tmp_dir zip_file unzip_cmd backup_file current_before current_after
|
local latest_output latest_rc latest_version download_url package_manager asset tmp_dir package_file current_before current_after
|
||||||
|
|
||||||
latest_output="$(get_xray_latest_info 2>/dev/null)"
|
latest_output="$(get_xray_latest_info 2>/dev/null)"
|
||||||
latest_rc=$?
|
latest_rc=$?
|
||||||
if [ $latest_rc -ne 0 ]; then
|
if [ $latest_rc -ne 0 ]; then
|
||||||
log_kv success 0
|
log_kv success 0
|
||||||
case "$latest_rc" in
|
case "$latest_rc" in
|
||||||
2) log_kv message 'Unsupported ARCH' ;;
|
2) log_kv message 'Unsupported package manager' ;;
|
||||||
3) log_kv message 'Failed to fetch release metadata' ;;
|
3) log_kv message 'Failed to fetch release metadata' ;;
|
||||||
4) log_kv message 'Matching release asset not found' ;;
|
4) log_kv message 'Matching release asset not found' ;;
|
||||||
*) log_kv message 'Unknown error' ;;
|
*) log_kv message 'Unknown error' ;;
|
||||||
@@ -1742,14 +1668,10 @@ xray_upgrade() {
|
|||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if ! unzip_cmd="$(select_unzip_cmd)"; then
|
|
||||||
log_kv success 0
|
|
||||||
log_kv message 'Missing unzip support'
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
latest_version="$(printf '%s\n' "$latest_output" | sed -n 's/^latest_version=//p' | sed -n '1p')"
|
latest_version="$(printf '%s\n' "$latest_output" | sed -n 's/^latest_version=//p' | sed -n '1p')"
|
||||||
download_url="$(printf '%s\n' "$latest_output" | sed -n 's/^download_url=//p' | sed -n '1p')"
|
download_url="$(printf '%s\n' "$latest_output" | sed -n 's/^download_url=//p' | sed -n '1p')"
|
||||||
|
package_manager="$(printf '%s\n' "$latest_output" | sed -n 's/^package_manager=//p' | sed -n '1p')"
|
||||||
|
asset="$(printf '%s\n' "$latest_output" | sed -n 's/^asset=//p' | sed -n '1p')"
|
||||||
current_before="$(get_xray_current_version 2>/dev/null || true)"
|
current_before="$(get_xray_current_version 2>/dev/null || true)"
|
||||||
if [ -n "$current_before" ] && ! version_gt "$latest_version" "$current_before"; then
|
if [ -n "$current_before" ] && ! version_gt "$latest_version" "$current_before"; then
|
||||||
log_kv success 1
|
log_kv success 1
|
||||||
@@ -1767,52 +1689,25 @@ xray_upgrade() {
|
|||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
zip_file="$tmp_dir/xray.zip"
|
package_file="$tmp_dir/$asset"
|
||||||
backup_file="$tmp_dir/xray.backup"
|
|
||||||
|
|
||||||
trap "rm -rf '$tmp_dir'" EXIT INT TERM
|
trap "rm -rf '$tmp_dir'" EXIT INT TERM
|
||||||
|
|
||||||
if ! download_file "$download_url" "$zip_file"; then
|
if ! download_file "$download_url" "$package_file"; then
|
||||||
log_kv success 0
|
log_kv success 0
|
||||||
log_kv message 'Download failed'
|
log_kv message 'Download failed'
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if ! sh -c "$unzip_cmd \"$zip_file\" -d \"$tmp_dir\" >/dev/null 2>&1"; then
|
if [ ! -s "$package_file" ] || ! mainprogram_install_package "$package_manager" "$package_file"; then
|
||||||
log_kv success 0
|
|
||||||
log_kv message 'Extract failed'
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ ! -f "$tmp_dir/xray" ]; then
|
|
||||||
log_kv success 0
|
|
||||||
log_kv message 'xray binary not found in archive'
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
chmod 0755 "$tmp_dir/xray" || true
|
|
||||||
if [ -x "$XRAY_BINARY" ]; then
|
|
||||||
cp -fp "$XRAY_BINARY" "$backup_file" 2>/dev/null || true
|
|
||||||
fi
|
|
||||||
|
|
||||||
if ! cp -f "$tmp_dir/xray" "$XRAY_BINARY"; then
|
|
||||||
if [ -f "$backup_file" ]; then
|
|
||||||
cp -f "$backup_file" "$XRAY_BINARY" 2>/dev/null || true
|
|
||||||
fi
|
|
||||||
log_kv success 0
|
log_kv success 0
|
||||||
log_kv message 'Install failed'
|
log_kv message 'Install failed'
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
chmod 0755 "$XRAY_BINARY" || true
|
|
||||||
current_after="$(get_xray_current_version 2>/dev/null || true)"
|
current_after="$(get_xray_current_version 2>/dev/null || true)"
|
||||||
if [ -z "$current_after" ]; then
|
if [ -z "$current_after" ]; then
|
||||||
if [ -f "$backup_file" ]; then
|
|
||||||
cp -f "$backup_file" "$XRAY_BINARY" 2>/dev/null || true
|
|
||||||
chmod 0755 "$XRAY_BINARY" || true
|
|
||||||
fi
|
|
||||||
log_kv success 0
|
log_kv success 0
|
||||||
log_kv message 'Installed binary failed to run'
|
log_kv message 'Installed package check failed'
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -1824,6 +1719,9 @@ xray_upgrade() {
|
|||||||
log_kv previous_version "$current_before"
|
log_kv previous_version "$current_before"
|
||||||
log_kv current_version "$current_after"
|
log_kv current_version "$current_after"
|
||||||
log_kv latest_version "$latest_version"
|
log_kv latest_version "$latest_version"
|
||||||
|
log_kv package_manager "$package_manager"
|
||||||
|
log_kv arch "$(get_openwrt_arch)"
|
||||||
|
log_kv asset "$asset"
|
||||||
log_kv message 'Upgrade completed'
|
log_kv message 'Upgrade completed'
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
@@ -1940,13 +1838,14 @@ mihomo_upgrade() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
naiveproxy_upgrade() {
|
naiveproxy_upgrade() {
|
||||||
local latest_output latest_rc latest_version download_url tmp_dir archive_file xz_cmd tar_cmd backup_file current_before current_after target_binary extracted_binary
|
local latest_output latest_rc latest_version download_url package_manager asset tmp_dir package_file current_before current_after
|
||||||
|
|
||||||
latest_output="$(get_naiveproxy_latest_info 2>/dev/null)"
|
latest_output="$(get_naiveproxy_latest_info 2>/dev/null)"
|
||||||
latest_rc=$?
|
latest_rc=$?
|
||||||
if [ $latest_rc -ne 0 ]; then
|
if [ $latest_rc -ne 0 ]; then
|
||||||
log_kv success 0
|
log_kv success 0
|
||||||
case "$latest_rc" in
|
case "$latest_rc" in
|
||||||
|
2) log_kv message 'Unsupported package manager' ;;
|
||||||
3) log_kv message 'Failed to fetch release metadata' ;;
|
3) log_kv message 'Failed to fetch release metadata' ;;
|
||||||
4) log_kv message 'Matching release asset not found' ;;
|
4) log_kv message 'Matching release asset not found' ;;
|
||||||
*) log_kv message 'Unknown error' ;;
|
*) log_kv message 'Unknown error' ;;
|
||||||
@@ -1954,20 +1853,10 @@ naiveproxy_upgrade() {
|
|||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if ! xz_cmd="$(select_xz_cmd)"; then
|
|
||||||
log_kv success 0
|
|
||||||
log_kv message 'Missing xz support'
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
if ! tar_cmd="$(select_tar_cmd)"; then
|
|
||||||
log_kv success 0
|
|
||||||
log_kv message 'Extract failed'
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
latest_version="$(printf '%s\n' "$latest_output" | sed -n 's/^latest_version=//p' | sed -n '1p')"
|
latest_version="$(printf '%s\n' "$latest_output" | sed -n 's/^latest_version=//p' | sed -n '1p')"
|
||||||
download_url="$(printf '%s\n' "$latest_output" | sed -n 's/^download_url=//p' | sed -n '1p')"
|
download_url="$(printf '%s\n' "$latest_output" | sed -n 's/^download_url=//p' | sed -n '1p')"
|
||||||
|
package_manager="$(printf '%s\n' "$latest_output" | sed -n 's/^package_manager=//p' | sed -n '1p')"
|
||||||
|
asset="$(printf '%s\n' "$latest_output" | sed -n 's/^asset=//p' | sed -n '1p')"
|
||||||
current_before="$(get_naiveproxy_current_version 2>/dev/null || true)"
|
current_before="$(get_naiveproxy_current_version 2>/dev/null || true)"
|
||||||
if [ -n "$current_before" ] && ! version_gt "$latest_version" "$current_before"; then
|
if [ -n "$current_before" ] && ! version_gt "$latest_version" "$current_before"; then
|
||||||
log_kv success 1
|
log_kv success 1
|
||||||
@@ -1978,9 +1867,6 @@ naiveproxy_upgrade() {
|
|||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
target_binary="$(find_naiveproxy_binary 2>/dev/null || true)"
|
|
||||||
[ -n "$target_binary" ] || target_binary="$NAIVEPROXY_BINARY"
|
|
||||||
|
|
||||||
tmp_dir="$(mktemp -d /tmp/ssrplus-naiveproxy.XXXXXX)"
|
tmp_dir="$(mktemp -d /tmp/ssrplus-naiveproxy.XXXXXX)"
|
||||||
if [ -z "$tmp_dir" ] || [ ! -d "$tmp_dir" ]; then
|
if [ -z "$tmp_dir" ] || [ ! -d "$tmp_dir" ]; then
|
||||||
log_kv success 0
|
log_kv success 0
|
||||||
@@ -1988,65 +1874,25 @@ naiveproxy_upgrade() {
|
|||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
archive_file="$tmp_dir/naiveproxy.tar.xz"
|
package_file="$tmp_dir/$asset"
|
||||||
backup_file="$tmp_dir/naive.backup"
|
|
||||||
extracted_binary=""
|
|
||||||
|
|
||||||
trap "rm -rf '$tmp_dir'" EXIT INT TERM
|
trap "rm -rf '$tmp_dir'" EXIT INT TERM
|
||||||
|
|
||||||
if ! download_file "$download_url" "$archive_file"; then
|
if ! download_file "$download_url" "$package_file"; then
|
||||||
log_kv success 0
|
log_kv success 0
|
||||||
log_kv message 'Download failed'
|
log_kv message 'Download failed'
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
mkdir -p "$tmp_dir/extract" || {
|
if [ ! -s "$package_file" ] || ! mainprogram_install_package "$package_manager" "$package_file"; then
|
||||||
log_kv success 0
|
|
||||||
log_kv message 'Failed to create temp directory'
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
if ! sh -c "cd \"$tmp_dir/extract\" && $xz_cmd \"$archive_file\" | $tar_cmd -xf - >/dev/null 2>&1"; then
|
|
||||||
log_kv success 0
|
|
||||||
log_kv message 'Extract failed'
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
extracted_binary="$(find "$tmp_dir/extract" -type f -name naive | sed -n '1p')"
|
|
||||||
if [ -z "$extracted_binary" ] || [ ! -s "$extracted_binary" ]; then
|
|
||||||
log_kv success 0
|
|
||||||
log_kv message 'naive binary not found in archive'
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
mkdir -p "$(dirname "$target_binary")" || true
|
|
||||||
chmod 0755 "$extracted_binary" || true
|
|
||||||
if [ -x "$target_binary" ]; then
|
|
||||||
cp -fp "$target_binary" "$backup_file" 2>/dev/null || true
|
|
||||||
fi
|
|
||||||
|
|
||||||
if ! cp -f "$extracted_binary" "$target_binary"; then
|
|
||||||
if [ -f "$backup_file" ]; then
|
|
||||||
cp -f "$backup_file" "$target_binary" 2>/dev/null || true
|
|
||||||
fi
|
|
||||||
log_kv success 0
|
log_kv success 0
|
||||||
log_kv message 'Install failed'
|
log_kv message 'Install failed'
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
chmod 0755 "$target_binary" || true
|
|
||||||
if [ "$target_binary" != "$NAIVEPROXY_BINARY" ] && [ ! -x "$NAIVEPROXY_BINARY" ]; then
|
|
||||||
ln -sf "$target_binary" "$NAIVEPROXY_BINARY" 2>/dev/null || true
|
|
||||||
fi
|
|
||||||
|
|
||||||
current_after="$(get_naiveproxy_current_version 2>/dev/null || true)"
|
current_after="$(get_naiveproxy_current_version 2>/dev/null || true)"
|
||||||
if [ -z "$current_after" ]; then
|
if [ -z "$current_after" ]; then
|
||||||
if [ -f "$backup_file" ]; then
|
|
||||||
cp -f "$backup_file" "$target_binary" 2>/dev/null || true
|
|
||||||
chmod 0755 "$target_binary" || true
|
|
||||||
fi
|
|
||||||
log_kv success 0
|
log_kv success 0
|
||||||
log_kv message 'Installed binary failed to run'
|
log_kv message 'Installed package check failed'
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -2058,6 +1904,9 @@ naiveproxy_upgrade() {
|
|||||||
log_kv previous_version "$current_before"
|
log_kv previous_version "$current_before"
|
||||||
log_kv current_version "$current_after"
|
log_kv current_version "$current_after"
|
||||||
log_kv latest_version "$latest_version"
|
log_kv latest_version "$latest_version"
|
||||||
|
log_kv package_manager "$package_manager"
|
||||||
|
log_kv arch "$(get_openwrt_arch)"
|
||||||
|
log_kv asset "$asset"
|
||||||
log_kv message 'Upgrade completed'
|
log_kv message 'Upgrade completed'
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
# Copyright (C) 2020 Gyj1109
|
||||||
|
# 适配 OpenWrt 25.12 修改版
|
||||||
|
|
||||||
|
include $(TOPDIR)/rules.mk
|
||||||
|
|
||||||
|
PKG_NAME:=luci-app-syncthing
|
||||||
|
PKG_VERSION:=1.0
|
||||||
|
PKG_RELEASE:=3
|
||||||
|
|
||||||
|
LUCI_TITLE:=LuCI support for Syncthing
|
||||||
|
LUCI_PKGARCH:=all
|
||||||
|
# 适配 25.12:确保安装了 syncthing 后,LuCI 界面能正确拉起
|
||||||
|
LUCI_DEPENDS:=+syncthing
|
||||||
|
|
||||||
|
include $(TOPDIR)/feeds/luci/luci.mk
|
||||||
|
|
||||||
|
# 明确定义配置文件的归属,这对 25.12 的 apk 管理器非常重要
|
||||||
|
define Package/$(PKG_NAME)/conffiles
|
||||||
|
/etc/config/syncthing
|
||||||
|
endef
|
||||||
|
|
||||||
|
$(eval $(call BuildPackage,$(PKG_NAME)))
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
# luci-app-syncthing
|
||||||
|
在Potat0000源码基础上进行了汉化优化,官方openwrt23.05.3编译后可使用
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
module("luci.controller.syncthing", package.seeall)
|
||||||
|
|
||||||
|
function index()
|
||||||
|
if not nixio.fs.access("/etc/config/syncthing") then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
entry({"admin", "services", "syncthing"}, cbi("syncthing"), _("文件同步"), 10).dependent = true
|
||||||
|
entry({"admin", "services", "syncthing", "status"}, call("act_status")).leaf = true
|
||||||
|
end
|
||||||
|
|
||||||
|
function act_status()
|
||||||
|
local e = {}
|
||||||
|
e.running = luci.sys.call("pgrep syncthing >/dev/null") == 0
|
||||||
|
luci.http.prepare_content("application/json")
|
||||||
|
luci.http.write_json(e)
|
||||||
|
end
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
require("nixio.fs")
|
||||||
|
|
||||||
|
m = Map("syncthing", translate("Syncthing同步工具"))
|
||||||
|
|
||||||
|
m:section(SimpleSection).template = "syncthing/syncthing_status"
|
||||||
|
|
||||||
|
s = m:section(TypedSection, "syncthing")
|
||||||
|
|
||||||
|
s.anonymous = true
|
||||||
|
|
||||||
|
o = s:option(Flag, "enabled", translate("启用"))
|
||||||
|
o.default = 0
|
||||||
|
o.rmempty = false
|
||||||
|
|
||||||
|
gui_address = s:option(Value, "gui_address", translate("GUI访问地址"))
|
||||||
|
gui_address.description = translate("使用0.0.0.0以监控所有访问。")
|
||||||
|
gui_address.default = "http://0.0.0.0:8384"
|
||||||
|
gui_address.placeholder = "http://0.0.0.0:8384"
|
||||||
|
gui_address.rmempty = false
|
||||||
|
|
||||||
|
home = s:option(Value, "home", translate("配置文件目录"))
|
||||||
|
home.description = translate("只有保存在/etc/syncthing中的配置会自动备份!")
|
||||||
|
home.default = "/etc/syncthing"
|
||||||
|
home.placeholder = "/etc/syncthing"
|
||||||
|
home.rmempty = false
|
||||||
|
|
||||||
|
user = s:option(ListValue, "user", translate("用户"))
|
||||||
|
user.description = translate("默认是syncthing,但这可能会导致权限被拒绝。Syncthing官方不建议以root身份运行。")
|
||||||
|
user:value("", translate("syncthing"))
|
||||||
|
for u in luci.util.execi("cat /etc/passwd | cut -d ':' -f1") do
|
||||||
|
user:value(u)
|
||||||
|
end
|
||||||
|
|
||||||
|
macprocs = s:option(Value, "macprocs", translate("线程限制"))
|
||||||
|
macprocs.description = translate("0表示匹配CPU数量(默认),>0表示显式指定并发数。")
|
||||||
|
macprocs.default = "0"
|
||||||
|
macprocs.placeholder = "0"
|
||||||
|
macprocs.datatype = "range(0,32)"
|
||||||
|
macprocs.rmempty = false
|
||||||
|
|
||||||
|
nice = s:option(Value, "nice", translate("优先级"))
|
||||||
|
nice.description = translate("显式指定优先级值。0是最高,19是最低。(暂时不允许设置负值)")
|
||||||
|
nice.default = "19"
|
||||||
|
nice.placeholder = "19"
|
||||||
|
nice.datatype = "range(0,19)"
|
||||||
|
nice.rmempty = false
|
||||||
|
|
||||||
|
return m
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
<script type="text/javascript">//<![CDATA[
|
||||||
|
XHR.poll(1, '<%=url([[admin]], [[services]], [[syncthing]], [[status]])%>', null,
|
||||||
|
function (x, data) {
|
||||||
|
var tb = document.getElementById('syncthing_status');
|
||||||
|
if (data && tb) {
|
||||||
|
if (data.running) {
|
||||||
|
var links = '<em><b><font color="green">Syncthing <%:运行中%></font></b></em><input class="btn cbi-button mar-10" type="button" value="<%:打开Syncthing页面%>" onclick="openwebui();" />';
|
||||||
|
tb.innerHTML = links;
|
||||||
|
} else {
|
||||||
|
tb.innerHTML = '<em><b><font color="red">Syncthing <%:未运行%></font></b></em>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
function openwebui(){
|
||||||
|
var url = window.location.host+":<%=luci.sys.exec("uci -q get syncthing.syncthing.gui_address"):match(":[0-9]+"):gsub(":", "")%>";
|
||||||
|
window.open('http://'+url,'target','');
|
||||||
|
}
|
||||||
|
//]]>
|
||||||
|
</script>
|
||||||
|
<style>.mar-10 {margin-left: 50px; margin-right: 10px;}</style>
|
||||||
|
<fieldset class="cbi-section">
|
||||||
|
<p id="syncthing_status">
|
||||||
|
<em><%:正在收集数据...%></em>
|
||||||
|
</p>
|
||||||
|
</fieldset>
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
touch /etc/config/syncthing
|
||||||
|
|
||||||
|
uci -q batch <<-EOF >/dev/null
|
||||||
|
delete ucitrack.@syncthing[-1]
|
||||||
|
add ucitrack syncthing
|
||||||
|
set ucitrack.@syncthing[-1].exec='/etc/init.d/syncthing stop && /etc/init.d/syncthing start'
|
||||||
|
commit ucitrack
|
||||||
|
EOF
|
||||||
|
# remove LuCI cache
|
||||||
|
rm -f /tmp/luci*
|
||||||
|
exit 0
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"luci-app-syncthing": {
|
||||||
|
"description": "Grant UCI access for luci-app-syncthing",
|
||||||
|
"read": {
|
||||||
|
"uci": [ "syncthing" ]
|
||||||
|
},
|
||||||
|
"write": {
|
||||||
|
"uci": [ "syncthing" ]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||