mirror of
https://github.com/clash-verge-rev/clash-verge-rev.git
synced 2026-10-15 13:50:47 +08:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ceea4f72f8 | ||
|
|
6aca27ea97 | ||
|
|
71ae357be8 | ||
|
|
2bbcdb1d95 | ||
|
|
646df28d1c | ||
|
|
f5f2ee1f95 | ||
|
|
0842c7343d |
+22
-5
@@ -1,4 +1,4 @@
|
||||
## v2.2.1
|
||||
## v2.2.2
|
||||
|
||||
**发行代号:拓**
|
||||
|
||||
@@ -6,8 +6,24 @@
|
||||
|
||||
代号释义: 本次发布在功能上的大幅扩展。新首页设计为用户带来全新交互体验,DNS 覆写功能增强网络控制能力,解锁测试页面助力内容访问自由度提升,轻量模式提供灵活使用选择。此外,macOS 应用菜单集成、sidecar 模式、诊断信息导出等新特性进一步丰富了软件的适用场景。这些新增功能显著拓宽了 Clash Verge 的功能边界,为用户提供了更强大的工具和可能性。
|
||||
|
||||
2.2.1 相对于 2.2.0(已下架不在提供)
|
||||
修复了:
|
||||
#### 已知问题
|
||||
- 仅在Ubuntu 22.04/24.04,Fedora 41 **Gnome桌面环境** 做过简单测试,不保证其他其他Linux发行版可用,将在未来做进一步适配和调优
|
||||
|
||||
### 2.2.2 相对于 2.2.1(已下架不在提供)
|
||||
#### 修复了:
|
||||
- 弹黑框的问题(原因是服务崩溃触发重装机制)
|
||||
- MacOS进入轻量模式以后影藏Dock图标
|
||||
- 增加轻量模式缺失的tray翻译
|
||||
- Linux下的窗口边框被削掉的问题
|
||||
|
||||
#### 新增了:
|
||||
- 加强服务检测和重装逻辑
|
||||
- 增强内核与服务保活机制
|
||||
- 增加服务模式下的僵尸进程清理机制
|
||||
- 新增当服务模式多次尝试失败后自动回退至用户空间模式
|
||||
|
||||
### 2.2.1 相对于 2.2.0(已下架不在提供)
|
||||
#### 修复了:
|
||||
1. **首页**
|
||||
- 修复 Direct 模式首页无法渲染
|
||||
- 修复 首页启用轻量模式导致 ClashVergeRev 从托盘退出
|
||||
@@ -23,7 +39,7 @@
|
||||
4. **轻量模式**
|
||||
- 修复 MacOS 轻量模式下 Dock 栏图标无法隐藏。
|
||||
|
||||
新增了:
|
||||
#### 新增了:
|
||||
1. **首页**
|
||||
- 首页文本过长自动截断
|
||||
2. **轻量模式**
|
||||
@@ -37,7 +53,7 @@
|
||||
|
||||
---
|
||||
|
||||
## v2.2.0(已下架不在提供)
|
||||
## 2.2.0(已下架不在提供)
|
||||
|
||||
#### 新增功能
|
||||
1. **首页**
|
||||
@@ -78,6 +94,7 @@
|
||||
- 修复 macOS tray图标错位到左上角的问题。
|
||||
- 修复 Windows/Linux 运行时崩溃。
|
||||
- 修复 Win10 阴影和边框问题。
|
||||
- 修复 升级或重装后开机自启状态检测和同步问题。
|
||||
|
||||
2. **构建**
|
||||
- 修复构建失败问题。
|
||||
|
||||
+2
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "clash-verge",
|
||||
"version": "2.2.1",
|
||||
"version": "--help",
|
||||
"license": "GPL-3.0-only",
|
||||
"scripts": {
|
||||
"dev": "cross-env RUST_BACKTRACE=1 tauri dev -f verge-dev -- --profile fast-dev",
|
||||
@@ -17,6 +17,7 @@
|
||||
"portable": "node scripts/portable.mjs",
|
||||
"portable-fixed-webview2": "node scripts/portable-fixed-webview2.mjs",
|
||||
"fix-alpha-version": "node scripts/fix-alpha_version.mjs",
|
||||
"release-version": "node scripts/release_version.mjs",
|
||||
"release-alpha-version": "node scripts/release-alpha_version.mjs",
|
||||
"prepare": "husky",
|
||||
"clean": "cd ./src-tauri && cargo clean && cd -"
|
||||
|
||||
+221
-210
@@ -8,8 +8,8 @@ const UPDATE_JSON_FILE = "update.json";
|
||||
const UPDATE_JSON_PROXY = "update-proxy.json";
|
||||
// Add alpha update JSON filenames
|
||||
const ALPHA_TAG_NAME = "updater-alpha";
|
||||
const ALPHA_UPDATE_JSON_FILE = "update-alpha.json";
|
||||
const ALPHA_UPDATE_JSON_PROXY = "update-alpha-proxy.json";
|
||||
const ALPHA_UPDATE_JSON_FILE = "update.json";
|
||||
const ALPHA_UPDATE_JSON_PROXY = "update-proxy.json";
|
||||
|
||||
/// generate update.json
|
||||
/// upload to update tag's release asset
|
||||
@@ -78,224 +78,235 @@ async function resolveUpdater() {
|
||||
async function processRelease(github, options, tag, isAlpha) {
|
||||
if (!tag) return;
|
||||
|
||||
const { data: release } = await github.rest.repos.getReleaseByTag({
|
||||
...options,
|
||||
tag: tag.name,
|
||||
});
|
||||
|
||||
const updateData = {
|
||||
name: tag.name,
|
||||
notes: await resolveUpdateLog(tag.name).catch(() =>
|
||||
resolveUpdateLogDefault().catch(() => "No changelog available"),
|
||||
),
|
||||
pub_date: new Date().toISOString(),
|
||||
platforms: {
|
||||
win64: { signature: "", url: "" }, // compatible with older formats
|
||||
linux: { signature: "", url: "" }, // compatible with older formats
|
||||
darwin: { signature: "", url: "" }, // compatible with older formats
|
||||
"darwin-aarch64": { signature: "", url: "" },
|
||||
"darwin-intel": { signature: "", url: "" },
|
||||
"darwin-x86_64": { signature: "", url: "" },
|
||||
"linux-x86_64": { signature: "", url: "" },
|
||||
"linux-x86": { signature: "", url: "" },
|
||||
"linux-i686": { signature: "", url: "" },
|
||||
"linux-aarch64": { signature: "", url: "" },
|
||||
"linux-armv7": { signature: "", url: "" },
|
||||
"windows-x86_64": { signature: "", url: "" },
|
||||
"windows-aarch64": { signature: "", url: "" },
|
||||
"windows-x86": { signature: "", url: "" },
|
||||
"windows-i686": { signature: "", url: "" },
|
||||
},
|
||||
};
|
||||
|
||||
const promises = release.assets.map(async (asset) => {
|
||||
const { name, browser_download_url } = asset;
|
||||
|
||||
// Process all the platform URL and signature data
|
||||
// win64 url
|
||||
if (name.endsWith("x64-setup.exe")) {
|
||||
updateData.platforms.win64.url = browser_download_url;
|
||||
updateData.platforms["windows-x86_64"].url = browser_download_url;
|
||||
}
|
||||
// win64 signature
|
||||
if (name.endsWith("x64-setup.exe.sig")) {
|
||||
const sig = await getSignature(browser_download_url);
|
||||
updateData.platforms.win64.signature = sig;
|
||||
updateData.platforms["windows-x86_64"].signature = sig;
|
||||
}
|
||||
|
||||
// win32 url
|
||||
if (name.endsWith("x86-setup.exe")) {
|
||||
updateData.platforms["windows-x86"].url = browser_download_url;
|
||||
updateData.platforms["windows-i686"].url = browser_download_url;
|
||||
}
|
||||
// win32 signature
|
||||
if (name.endsWith("x86-setup.exe.sig")) {
|
||||
const sig = await getSignature(browser_download_url);
|
||||
updateData.platforms["windows-x86"].signature = sig;
|
||||
updateData.platforms["windows-i686"].signature = sig;
|
||||
}
|
||||
|
||||
// win arm url
|
||||
if (name.endsWith("arm64-setup.exe")) {
|
||||
updateData.platforms["windows-aarch64"].url = browser_download_url;
|
||||
}
|
||||
// win arm signature
|
||||
if (name.endsWith("arm64-setup.exe.sig")) {
|
||||
const sig = await getSignature(browser_download_url);
|
||||
updateData.platforms["windows-aarch64"].signature = sig;
|
||||
}
|
||||
|
||||
// darwin url (intel)
|
||||
if (name.endsWith(".app.tar.gz") && !name.includes("aarch")) {
|
||||
updateData.platforms.darwin.url = browser_download_url;
|
||||
updateData.platforms["darwin-intel"].url = browser_download_url;
|
||||
updateData.platforms["darwin-x86_64"].url = browser_download_url;
|
||||
}
|
||||
// darwin signature (intel)
|
||||
if (name.endsWith(".app.tar.gz.sig") && !name.includes("aarch")) {
|
||||
const sig = await getSignature(browser_download_url);
|
||||
updateData.platforms.darwin.signature = sig;
|
||||
updateData.platforms["darwin-intel"].signature = sig;
|
||||
updateData.platforms["darwin-x86_64"].signature = sig;
|
||||
}
|
||||
|
||||
// darwin url (aarch)
|
||||
if (name.endsWith("aarch64.app.tar.gz")) {
|
||||
updateData.platforms["darwin-aarch64"].url = browser_download_url;
|
||||
// 使linux可以检查更新
|
||||
updateData.platforms.linux.url = browser_download_url;
|
||||
updateData.platforms["linux-x86_64"].url = browser_download_url;
|
||||
updateData.platforms["linux-x86"].url = browser_download_url;
|
||||
updateData.platforms["linux-i686"].url = browser_download_url;
|
||||
updateData.platforms["linux-aarch64"].url = browser_download_url;
|
||||
updateData.platforms["linux-armv7"].url = browser_download_url;
|
||||
}
|
||||
// darwin signature (aarch)
|
||||
if (name.endsWith("aarch64.app.tar.gz.sig")) {
|
||||
const sig = await getSignature(browser_download_url);
|
||||
updateData.platforms["darwin-aarch64"].signature = sig;
|
||||
updateData.platforms.linux.signature = sig;
|
||||
updateData.platforms["linux-x86_64"].signature = sig;
|
||||
updateData.platforms["linux-x86"].url = browser_download_url;
|
||||
updateData.platforms["linux-i686"].url = browser_download_url;
|
||||
updateData.platforms["linux-aarch64"].signature = sig;
|
||||
updateData.platforms["linux-armv7"].signature = sig;
|
||||
}
|
||||
});
|
||||
|
||||
await Promise.allSettled(promises);
|
||||
console.log(updateData);
|
||||
|
||||
// maybe should test the signature as well
|
||||
// delete the null field
|
||||
Object.entries(updateData.platforms).forEach(([key, value]) => {
|
||||
if (!value.url) {
|
||||
console.log(`[Error]: failed to parse release for "${key}"`);
|
||||
delete updateData.platforms[key];
|
||||
}
|
||||
});
|
||||
|
||||
// Generate a proxy update file for accelerated GitHub resources
|
||||
const updateDataNew = JSON.parse(JSON.stringify(updateData));
|
||||
|
||||
Object.entries(updateDataNew.platforms).forEach(([key, value]) => {
|
||||
if (value.url) {
|
||||
updateDataNew.platforms[key].url =
|
||||
"https://download.clashverge.dev/" + value.url;
|
||||
} else {
|
||||
console.log(`[Error]: updateDataNew.platforms.${key} is null`);
|
||||
}
|
||||
});
|
||||
|
||||
// Get the appropriate updater release based on isAlpha flag
|
||||
const releaseTag = isAlpha ? ALPHA_TAG_NAME : UPDATE_TAG_NAME;
|
||||
console.log(
|
||||
`Processing ${isAlpha ? "alpha" : "stable"} release:`,
|
||||
releaseTag,
|
||||
);
|
||||
|
||||
try {
|
||||
let updateRelease;
|
||||
const { data: release } = await github.rest.repos.getReleaseByTag({
|
||||
...options,
|
||||
tag: tag.name,
|
||||
});
|
||||
|
||||
const updateData = {
|
||||
name: tag.name,
|
||||
notes: await resolveUpdateLog(tag.name).catch(() =>
|
||||
resolveUpdateLogDefault().catch(() => "No changelog available"),
|
||||
),
|
||||
pub_date: new Date().toISOString(),
|
||||
platforms: {
|
||||
win64: { signature: "", url: "" }, // compatible with older formats
|
||||
linux: { signature: "", url: "" }, // compatible with older formats
|
||||
darwin: { signature: "", url: "" }, // compatible with older formats
|
||||
"darwin-aarch64": { signature: "", url: "" },
|
||||
"darwin-intel": { signature: "", url: "" },
|
||||
"darwin-x86_64": { signature: "", url: "" },
|
||||
"linux-x86_64": { signature: "", url: "" },
|
||||
"linux-x86": { signature: "", url: "" },
|
||||
"linux-i686": { signature: "", url: "" },
|
||||
"linux-aarch64": { signature: "", url: "" },
|
||||
"linux-armv7": { signature: "", url: "" },
|
||||
"windows-x86_64": { signature: "", url: "" },
|
||||
"windows-aarch64": { signature: "", url: "" },
|
||||
"windows-x86": { signature: "", url: "" },
|
||||
"windows-i686": { signature: "", url: "" },
|
||||
},
|
||||
};
|
||||
|
||||
const promises = release.assets.map(async (asset) => {
|
||||
const { name, browser_download_url } = asset;
|
||||
|
||||
// Process all the platform URL and signature data
|
||||
// win64 url
|
||||
if (name.endsWith("x64-setup.exe")) {
|
||||
updateData.platforms.win64.url = browser_download_url;
|
||||
updateData.platforms["windows-x86_64"].url = browser_download_url;
|
||||
}
|
||||
// win64 signature
|
||||
if (name.endsWith("x64-setup.exe.sig")) {
|
||||
const sig = await getSignature(browser_download_url);
|
||||
updateData.platforms.win64.signature = sig;
|
||||
updateData.platforms["windows-x86_64"].signature = sig;
|
||||
}
|
||||
|
||||
// win32 url
|
||||
if (name.endsWith("x86-setup.exe")) {
|
||||
updateData.platforms["windows-x86"].url = browser_download_url;
|
||||
updateData.platforms["windows-i686"].url = browser_download_url;
|
||||
}
|
||||
// win32 signature
|
||||
if (name.endsWith("x86-setup.exe.sig")) {
|
||||
const sig = await getSignature(browser_download_url);
|
||||
updateData.platforms["windows-x86"].signature = sig;
|
||||
updateData.platforms["windows-i686"].signature = sig;
|
||||
}
|
||||
|
||||
// win arm url
|
||||
if (name.endsWith("arm64-setup.exe")) {
|
||||
updateData.platforms["windows-aarch64"].url = browser_download_url;
|
||||
}
|
||||
// win arm signature
|
||||
if (name.endsWith("arm64-setup.exe.sig")) {
|
||||
const sig = await getSignature(browser_download_url);
|
||||
updateData.platforms["windows-aarch64"].signature = sig;
|
||||
}
|
||||
|
||||
// darwin url (intel)
|
||||
if (name.endsWith(".app.tar.gz") && !name.includes("aarch")) {
|
||||
updateData.platforms.darwin.url = browser_download_url;
|
||||
updateData.platforms["darwin-intel"].url = browser_download_url;
|
||||
updateData.platforms["darwin-x86_64"].url = browser_download_url;
|
||||
}
|
||||
// darwin signature (intel)
|
||||
if (name.endsWith(".app.tar.gz.sig") && !name.includes("aarch")) {
|
||||
const sig = await getSignature(browser_download_url);
|
||||
updateData.platforms.darwin.signature = sig;
|
||||
updateData.platforms["darwin-intel"].signature = sig;
|
||||
updateData.platforms["darwin-x86_64"].signature = sig;
|
||||
}
|
||||
|
||||
// darwin url (aarch)
|
||||
if (name.endsWith("aarch64.app.tar.gz")) {
|
||||
updateData.platforms["darwin-aarch64"].url = browser_download_url;
|
||||
// 使linux可以检查更新
|
||||
updateData.platforms.linux.url = browser_download_url;
|
||||
updateData.platforms["linux-x86_64"].url = browser_download_url;
|
||||
updateData.platforms["linux-x86"].url = browser_download_url;
|
||||
updateData.platforms["linux-i686"].url = browser_download_url;
|
||||
updateData.platforms["linux-aarch64"].url = browser_download_url;
|
||||
updateData.platforms["linux-armv7"].url = browser_download_url;
|
||||
}
|
||||
// darwin signature (aarch)
|
||||
if (name.endsWith("aarch64.app.tar.gz.sig")) {
|
||||
const sig = await getSignature(browser_download_url);
|
||||
updateData.platforms["darwin-aarch64"].signature = sig;
|
||||
updateData.platforms.linux.signature = sig;
|
||||
updateData.platforms["linux-x86_64"].signature = sig;
|
||||
updateData.platforms["linux-x86"].url = browser_download_url;
|
||||
updateData.platforms["linux-i686"].url = browser_download_url;
|
||||
updateData.platforms["linux-aarch64"].signature = sig;
|
||||
updateData.platforms["linux-armv7"].signature = sig;
|
||||
}
|
||||
});
|
||||
|
||||
await Promise.allSettled(promises);
|
||||
console.log(updateData);
|
||||
|
||||
// maybe should test the signature as well
|
||||
// delete the null field
|
||||
Object.entries(updateData.platforms).forEach(([key, value]) => {
|
||||
if (!value.url) {
|
||||
console.log(`[Error]: failed to parse release for "${key}"`);
|
||||
delete updateData.platforms[key];
|
||||
}
|
||||
});
|
||||
|
||||
// Generate a proxy update file for accelerated GitHub resources
|
||||
const updateDataNew = JSON.parse(JSON.stringify(updateData));
|
||||
|
||||
Object.entries(updateDataNew.platforms).forEach(([key, value]) => {
|
||||
if (value.url) {
|
||||
updateDataNew.platforms[key].url =
|
||||
"https://download.clashverge.dev/" + value.url;
|
||||
} else {
|
||||
console.log(`[Error]: updateDataNew.platforms.${key} is null`);
|
||||
}
|
||||
});
|
||||
|
||||
// Get the appropriate updater release based on isAlpha flag
|
||||
const releaseTag = isAlpha ? ALPHA_TAG_NAME : UPDATE_TAG_NAME;
|
||||
console.log(
|
||||
`Processing ${isAlpha ? "alpha" : "stable"} release:`,
|
||||
releaseTag,
|
||||
);
|
||||
|
||||
try {
|
||||
// Try to get the existing release
|
||||
const response = await github.rest.repos.getReleaseByTag({
|
||||
let updateRelease;
|
||||
|
||||
try {
|
||||
// Try to get the existing release
|
||||
const response = await github.rest.repos.getReleaseByTag({
|
||||
...options,
|
||||
tag: releaseTag,
|
||||
});
|
||||
updateRelease = response.data;
|
||||
console.log(
|
||||
`Found existing ${releaseTag} release with ID: ${updateRelease.id}`,
|
||||
);
|
||||
} catch (error) {
|
||||
// If release doesn't exist, create it
|
||||
if (error.status === 404) {
|
||||
console.log(
|
||||
`Release with tag ${releaseTag} not found, creating new release...`,
|
||||
);
|
||||
const createResponse = await github.rest.repos.createRelease({
|
||||
...options,
|
||||
tag_name: releaseTag,
|
||||
name: isAlpha
|
||||
? "Auto-update Alpha Channel"
|
||||
: "Auto-update Stable Channel",
|
||||
body: `This release contains the update information for ${isAlpha ? "alpha" : "stable"} channel.`,
|
||||
prerelease: isAlpha,
|
||||
});
|
||||
updateRelease = createResponse.data;
|
||||
console.log(
|
||||
`Created new ${releaseTag} release with ID: ${updateRelease.id}`,
|
||||
);
|
||||
} else {
|
||||
// If it's another error, throw it
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// File names based on release type
|
||||
const jsonFile = isAlpha ? ALPHA_UPDATE_JSON_FILE : UPDATE_JSON_FILE;
|
||||
const proxyFile = isAlpha ? ALPHA_UPDATE_JSON_PROXY : UPDATE_JSON_PROXY;
|
||||
|
||||
// Delete existing assets with these names
|
||||
for (let asset of updateRelease.assets) {
|
||||
if (asset.name === jsonFile) {
|
||||
await github.rest.repos.deleteReleaseAsset({
|
||||
...options,
|
||||
asset_id: asset.id,
|
||||
});
|
||||
}
|
||||
|
||||
if (asset.name === proxyFile) {
|
||||
await github.rest.repos
|
||||
.deleteReleaseAsset({ ...options, asset_id: asset.id })
|
||||
.catch(console.error); // do not break the pipeline
|
||||
}
|
||||
}
|
||||
|
||||
// Upload new assets
|
||||
await github.rest.repos.uploadReleaseAsset({
|
||||
...options,
|
||||
tag: releaseTag,
|
||||
release_id: updateRelease.id,
|
||||
name: jsonFile,
|
||||
data: JSON.stringify(updateData, null, 2),
|
||||
});
|
||||
updateRelease = response.data;
|
||||
|
||||
await github.rest.repos.uploadReleaseAsset({
|
||||
...options,
|
||||
release_id: updateRelease.id,
|
||||
name: proxyFile,
|
||||
data: JSON.stringify(updateDataNew, null, 2),
|
||||
});
|
||||
|
||||
console.log(
|
||||
`Found existing ${releaseTag} release with ID: ${updateRelease.id}`,
|
||||
`Successfully uploaded ${isAlpha ? "alpha" : "stable"} update files to ${releaseTag}`,
|
||||
);
|
||||
} catch (error) {
|
||||
// If release doesn't exist, create it
|
||||
if (error.status === 404) {
|
||||
console.log(
|
||||
`Release with tag ${releaseTag} not found, creating new release...`,
|
||||
);
|
||||
const createResponse = await github.rest.repos.createRelease({
|
||||
...options,
|
||||
tag_name: releaseTag,
|
||||
name: isAlpha
|
||||
? "Auto-update Alpha Channel"
|
||||
: "Auto-update Stable Channel",
|
||||
body: `This release contains the update information for ${isAlpha ? "alpha" : "stable"} channel.`,
|
||||
prerelease: isAlpha,
|
||||
});
|
||||
updateRelease = createResponse.data;
|
||||
console.log(
|
||||
`Created new ${releaseTag} release with ID: ${updateRelease.id}`,
|
||||
);
|
||||
} else {
|
||||
// If it's another error, throw it
|
||||
throw error;
|
||||
}
|
||||
console.error(
|
||||
`Failed to process ${isAlpha ? "alpha" : "stable"} release:`,
|
||||
error.message,
|
||||
);
|
||||
}
|
||||
|
||||
// File names based on release type
|
||||
const jsonFile = isAlpha ? ALPHA_UPDATE_JSON_FILE : UPDATE_JSON_FILE;
|
||||
const proxyFile = isAlpha ? ALPHA_UPDATE_JSON_PROXY : UPDATE_JSON_PROXY;
|
||||
|
||||
// Delete existing assets with these names
|
||||
for (let asset of updateRelease.assets) {
|
||||
if (asset.name === jsonFile) {
|
||||
await github.rest.repos.deleteReleaseAsset({
|
||||
...options,
|
||||
asset_id: asset.id,
|
||||
});
|
||||
}
|
||||
|
||||
if (asset.name === proxyFile) {
|
||||
await github.rest.repos
|
||||
.deleteReleaseAsset({ ...options, asset_id: asset.id })
|
||||
.catch(console.error); // do not break the pipeline
|
||||
}
|
||||
}
|
||||
|
||||
// Upload new assets
|
||||
await github.rest.repos.uploadReleaseAsset({
|
||||
...options,
|
||||
release_id: updateRelease.id,
|
||||
name: jsonFile,
|
||||
data: JSON.stringify(updateData, null, 2),
|
||||
});
|
||||
|
||||
await github.rest.repos.uploadReleaseAsset({
|
||||
...options,
|
||||
release_id: updateRelease.id,
|
||||
name: proxyFile,
|
||||
data: JSON.stringify(updateDataNew, null, 2),
|
||||
});
|
||||
|
||||
console.log(
|
||||
`Successfully uploaded ${isAlpha ? "alpha" : "stable"} update files to ${releaseTag}`,
|
||||
);
|
||||
} catch (error) {
|
||||
console.error(
|
||||
`Failed to process ${isAlpha ? "alpha" : "stable"} release:`,
|
||||
error.message,
|
||||
);
|
||||
if (error.status === 404) {
|
||||
console.log(`Release not found for tag: ${tag.name}, skipping...`);
|
||||
} else {
|
||||
console.error(
|
||||
`Failed to get release for tag: ${tag.name}`,
|
||||
error.message,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Generated
+1
-1
@@ -1132,7 +1132,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clash-verge"
|
||||
version = "2.2.1"
|
||||
version = "2.2.2"
|
||||
dependencies = [
|
||||
"ab_glyph",
|
||||
"aes-gcm",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "clash-verge"
|
||||
version = "2.2.1"
|
||||
version = "2.2.2"
|
||||
description = "clash verge"
|
||||
authors = ["zzzgydi", "wonfen", "MystiPanda"]
|
||||
license = "GPL-3.0-only"
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
use super::CmdResult;
|
||||
use crate::{core::CoreManager, module::mihomo::MihomoManager};
|
||||
use crate::module::mihomo::MihomoManager;
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn get_proxies() -> CmdResult<serde_json::Value> {
|
||||
CoreManager::global().ensure_running_core().await;
|
||||
let mannager = MihomoManager::global();
|
||||
let proxies = mannager
|
||||
.refresh_proxies()
|
||||
@@ -15,7 +14,6 @@ pub async fn get_proxies() -> CmdResult<serde_json::Value> {
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn get_providers_proxies() -> CmdResult<serde_json::Value> {
|
||||
CoreManager::global().ensure_running_core().await;
|
||||
let mannager = MihomoManager::global();
|
||||
let providers = mannager
|
||||
.refresh_providers_proxies()
|
||||
|
||||
@@ -47,7 +47,7 @@ pub async fn get_system_info() -> CmdResult<String> {
|
||||
pub async fn get_running_mode() -> Result<String, String> {
|
||||
match CoreManager::global().get_running_mode().await {
|
||||
core::RunningMode::Service => Ok("service".to_string()),
|
||||
core::RunningMode::Sidecar => Ok("sidecar".to_string()),
|
||||
core::RunningMode::Sidecar => Ok("standalone".to_string()),
|
||||
core::RunningMode::NotRunning => Ok("not_running".to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
+351
-803
File diff suppressed because it is too large
Load Diff
@@ -3,15 +3,11 @@ use once_cell::sync::OnceCell;
|
||||
use parking_lot::RwLock;
|
||||
use std::sync::Arc;
|
||||
use tauri::{AppHandle, Emitter, Manager, WebviewWindow};
|
||||
use tauri_plugin_shell::process::CommandChild;
|
||||
use std::fs::File;
|
||||
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct Handle {
|
||||
pub app_handle: Arc<RwLock<Option<AppHandle>>>,
|
||||
pub is_exiting: Arc<RwLock<bool>>,
|
||||
pub core_process: Arc<RwLock<Option<CommandChild>>>,
|
||||
pub core_lock: Arc<RwLock<Option<File>>>,
|
||||
}
|
||||
|
||||
impl Handle {
|
||||
@@ -21,8 +17,6 @@ impl Handle {
|
||||
HANDLE.get_or_init(|| Handle {
|
||||
app_handle: Arc::new(RwLock::new(None)),
|
||||
is_exiting: Arc::new(RwLock::new(false)),
|
||||
core_process: Arc::new(RwLock::new(None)),
|
||||
core_lock: Arc::new(RwLock::new(None)),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -74,39 +68,7 @@ impl Handle {
|
||||
*is_exiting = true;
|
||||
}
|
||||
|
||||
pub fn set_core_process(&self, process: CommandChild) {
|
||||
let mut core_process = self.core_process.write();
|
||||
*core_process = Some(process);
|
||||
}
|
||||
|
||||
pub fn take_core_process(&self) -> Option<CommandChild> {
|
||||
let mut core_process = self.core_process.write();
|
||||
core_process.take()
|
||||
}
|
||||
|
||||
/// 检查是否有运行中的核心进程
|
||||
pub fn has_core_process(&self) -> bool {
|
||||
self.core_process.read().is_some()
|
||||
}
|
||||
|
||||
pub fn is_exiting(&self) -> bool {
|
||||
*self.is_exiting.read()
|
||||
}
|
||||
|
||||
/// 设置核心文件锁
|
||||
pub fn set_core_lock(&self, file: File) {
|
||||
let mut core_lock = self.core_lock.write();
|
||||
*core_lock = Some(file);
|
||||
}
|
||||
|
||||
/// 释放核心文件锁
|
||||
pub fn release_core_lock(&self) -> Option<File> {
|
||||
let mut core_lock = self.core_lock.write();
|
||||
core_lock.take()
|
||||
}
|
||||
|
||||
/// 检查是否持有核心文件锁
|
||||
pub fn has_core_lock(&self) -> bool {
|
||||
self.core_lock.read().is_some()
|
||||
}
|
||||
}
|
||||
|
||||
+113
-50
@@ -1,6 +1,9 @@
|
||||
use crate::{
|
||||
config::Config, core::handle, feat, log_err, module::lightweight::entry_lightweight_mode,
|
||||
utils::resolve,
|
||||
config::Config,
|
||||
core::handle,
|
||||
feat, log_err, logging,
|
||||
module::lightweight::entry_lightweight_mode,
|
||||
utils::{logging::Type, resolve},
|
||||
};
|
||||
use anyhow::{bail, Result};
|
||||
use once_cell::sync::OnceCell;
|
||||
@@ -26,22 +29,27 @@ impl Hotkey {
|
||||
let verge = Config::verge();
|
||||
let enable_global_hotkey = verge.latest().enable_global_hotkey.unwrap_or(true);
|
||||
|
||||
println!(
|
||||
"Initializing hotkeys, global hotkey enabled: {}",
|
||||
logging!(
|
||||
info,
|
||||
Type::Hotkey,
|
||||
true,
|
||||
"Initializing hotkeys with enable: {}",
|
||||
enable_global_hotkey
|
||||
);
|
||||
log::info!(target: "app", "Initializing hotkeys, global hotkey enabled: {}", enable_global_hotkey);
|
||||
|
||||
// 如果全局热键被禁用,则不注册热键
|
||||
if !enable_global_hotkey {
|
||||
println!("Global hotkey is disabled, skipping registration");
|
||||
log::info!(target: "app", "Global hotkey is disabled, skipping registration");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if let Some(hotkeys) = verge.latest().hotkeys.as_ref() {
|
||||
println!("Found {} hotkeys to register", hotkeys.len());
|
||||
log::info!(target: "app", "Found {} hotkeys to register", hotkeys.len());
|
||||
logging!(
|
||||
info,
|
||||
Type::Hotkey,
|
||||
true,
|
||||
"Has {} hotkeys need to register",
|
||||
hotkeys.len()
|
||||
);
|
||||
|
||||
for hotkey in hotkeys.iter() {
|
||||
let mut iter = hotkey.split(',');
|
||||
@@ -50,28 +58,52 @@ impl Hotkey {
|
||||
|
||||
match (key, func) {
|
||||
(Some(key), Some(func)) => {
|
||||
println!("Registering hotkey: {} -> {}", key, func);
|
||||
log::info!(target: "app", "Registering hotkey: {} -> {}", key, func);
|
||||
logging!(
|
||||
info,
|
||||
Type::Hotkey,
|
||||
true,
|
||||
"Registering hotkey: {} -> {}",
|
||||
key,
|
||||
func
|
||||
);
|
||||
if let Err(e) = self.register(key, func) {
|
||||
println!("Failed to register hotkey {} -> {}: {:?}", key, func, e);
|
||||
log::error!(target: "app", "Failed to register hotkey {} -> {}: {:?}", key, func, e);
|
||||
logging!(
|
||||
error,
|
||||
Type::Hotkey,
|
||||
true,
|
||||
"Failed to register hotkey {} -> {}: {:?}",
|
||||
key,
|
||||
func,
|
||||
e
|
||||
);
|
||||
} else {
|
||||
println!("Successfully registered hotkey {} -> {}", key, func);
|
||||
log::info!(target: "app", "Successfully registered hotkey {} -> {}", key, func);
|
||||
logging!(
|
||||
info,
|
||||
Type::Hotkey,
|
||||
true,
|
||||
"Successfully registered hotkey {} -> {}",
|
||||
key,
|
||||
func
|
||||
);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
let key = key.unwrap_or("None");
|
||||
let func = func.unwrap_or("None");
|
||||
println!("Invalid hotkey configuration: `{key}`:`{func}`");
|
||||
log::error!(target: "app", "Invalid hotkey configuration: `{key}`:`{func}`");
|
||||
logging!(
|
||||
error,
|
||||
Type::Hotkey,
|
||||
true,
|
||||
"Invalid hotkey configuration: `{}`:`{}`",
|
||||
key,
|
||||
func
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
self.current.lock().clone_from(hotkeys);
|
||||
} else {
|
||||
println!("No hotkeys configured");
|
||||
log::info!(target: "app", "No hotkeys configured");
|
||||
logging!(info, Type::Hotkey, true, "No hotkeys configured");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -88,45 +120,60 @@ impl Hotkey {
|
||||
let app_handle = handle::Handle::global().app_handle().unwrap();
|
||||
let manager = app_handle.global_shortcut();
|
||||
|
||||
println!(
|
||||
logging!(
|
||||
info,
|
||||
Type::Hotkey,
|
||||
true,
|
||||
"Attempting to register hotkey: {} for function: {}",
|
||||
hotkey, func
|
||||
hotkey,
|
||||
func
|
||||
);
|
||||
log::info!(target: "app", "Attempting to register hotkey: {} for function: {}", hotkey, func);
|
||||
|
||||
if manager.is_registered(hotkey) {
|
||||
println!(
|
||||
logging!(
|
||||
info,
|
||||
Type::Hotkey,
|
||||
true,
|
||||
"Hotkey {} was already registered, unregistering first",
|
||||
hotkey
|
||||
);
|
||||
log::info!(target: "app", "Hotkey {} was already registered, unregistering first", hotkey);
|
||||
manager.unregister(hotkey)?;
|
||||
}
|
||||
|
||||
let f = match func.trim() {
|
||||
"open_or_close_dashboard" => {
|
||||
println!("Registering open_or_close_dashboard function");
|
||||
log::info!(target: "app", "Registering open_or_close_dashboard function");
|
||||
logging!(
|
||||
info,
|
||||
Type::Hotkey,
|
||||
true,
|
||||
"Registering open_or_close_dashboard function"
|
||||
);
|
||||
|| {
|
||||
println!("=== Hotkey Dashboard Window Operation Start ===");
|
||||
log::info!(target: "app", "=== Hotkey Dashboard Window Operation Start ===");
|
||||
logging!(
|
||||
info,
|
||||
Type::Hotkey,
|
||||
true,
|
||||
"=== Hotkey Dashboard Window Operation Start ==="
|
||||
);
|
||||
|
||||
// 使用 spawn_blocking 来确保在正确的线程上执行
|
||||
async_runtime::spawn_blocking(|| {
|
||||
println!("Toggle dashboard window visibility");
|
||||
log::info!(target: "app", "Toggle dashboard window visibility");
|
||||
logging!(
|
||||
info,
|
||||
Type::Hotkey,
|
||||
true,
|
||||
"Toggle dashboard window visibility"
|
||||
);
|
||||
|
||||
// 检查窗口是否存在
|
||||
if let Some(window) = handle::Handle::global().get_window() {
|
||||
// 如果窗口可见,则隐藏它
|
||||
if window.is_visible().unwrap_or(false) {
|
||||
println!("Window is visible, hiding it");
|
||||
log::info!(target: "app", "Window is visible, hiding it");
|
||||
logging!(info, Type::Window, true, "Window is visible, hiding it");
|
||||
let _ = window.hide();
|
||||
} else {
|
||||
// 如果窗口不可见,则显示它
|
||||
println!("Window is hidden, showing it");
|
||||
log::info!(target: "app", "Window is hidden, showing it");
|
||||
logging!(info, Type::Window, true, "Window is hidden, showing it");
|
||||
if window.is_minimized().unwrap_or(false) {
|
||||
let _ = window.unminimize();
|
||||
}
|
||||
@@ -135,14 +182,22 @@ impl Hotkey {
|
||||
}
|
||||
} else {
|
||||
// 如果窗口不存在,创建一个新窗口
|
||||
println!("Window does not exist, creating a new one");
|
||||
log::info!(target: "app", "Window does not exist, creating a new one");
|
||||
logging!(
|
||||
info,
|
||||
Type::Window,
|
||||
true,
|
||||
"Window does not exist, creating a new one"
|
||||
);
|
||||
resolve::create_window();
|
||||
}
|
||||
});
|
||||
|
||||
println!("=== Hotkey Dashboard Window Operation End ===");
|
||||
log::info!(target: "app", "=== Hotkey Dashboard Window Operation End ===");
|
||||
logging!(
|
||||
info,
|
||||
Type::Hotkey,
|
||||
true,
|
||||
"=== Hotkey Dashboard Window Operation End ==="
|
||||
);
|
||||
}
|
||||
}
|
||||
"clash_mode_rule" => || feat::change_clash_mode("rule".into()),
|
||||
@@ -156,8 +211,7 @@ impl Hotkey {
|
||||
"hide" => || feat::hide(),
|
||||
|
||||
_ => {
|
||||
println!("Invalid function: {}", func);
|
||||
log::error!(target: "app", "Invalid function: {}", func);
|
||||
logging!(error, Type::Hotkey, true, "Invalid function: {}", func);
|
||||
bail!("invalid function \"{func}\"");
|
||||
}
|
||||
};
|
||||
@@ -166,21 +220,18 @@ impl Hotkey {
|
||||
|
||||
let _ = manager.on_shortcut(hotkey, move |app_handle, hotkey, event| {
|
||||
if event.state == ShortcutState::Pressed {
|
||||
println!("Hotkey pressed: {:?}", hotkey);
|
||||
log::info!(target: "app", "Hotkey pressed: {:?}", hotkey);
|
||||
logging!(info, Type::Hotkey, true, "Hotkey pressed: {:?}", hotkey);
|
||||
|
||||
if hotkey.key == Code::KeyQ && is_quit {
|
||||
if let Some(window) = app_handle.get_webview_window("main") {
|
||||
if window.is_focused().unwrap_or(false) {
|
||||
println!("Executing quit function");
|
||||
log::info!(target: "app", "Executing quit function");
|
||||
logging!(info, Type::Hotkey, true, "Executing quit function");
|
||||
f();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 直接执行函数,不做任何状态检查
|
||||
println!("Executing function directly");
|
||||
log::info!(target: "app", "Executing function directly");
|
||||
logging!(info, Type::Hotkey, true, "Executing function directly");
|
||||
|
||||
// 获取全局热键状态
|
||||
let is_enable_global_hotkey = Config::verge()
|
||||
@@ -203,8 +254,14 @@ impl Hotkey {
|
||||
}
|
||||
});
|
||||
|
||||
println!("Successfully registered hotkey {} for {}", hotkey, func);
|
||||
log::info!(target: "app", "Successfully registered hotkey {} for {}", hotkey, func);
|
||||
logging!(
|
||||
info,
|
||||
Type::Hotkey,
|
||||
true,
|
||||
"Successfully registered hotkey {} for {}",
|
||||
hotkey,
|
||||
func
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -212,7 +269,7 @@ impl Hotkey {
|
||||
let app_handle = handle::Handle::global().app_handle().unwrap();
|
||||
let manager = app_handle.global_shortcut();
|
||||
manager.unregister(hotkey)?;
|
||||
log::debug!(target: "app", "unregister hotkey {hotkey}");
|
||||
logging!(debug, Type::Hotkey, true, "Unregister hotkey {}", hotkey);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -285,7 +342,13 @@ impl Drop for Hotkey {
|
||||
fn drop(&mut self) {
|
||||
let app_handle = handle::Handle::global().app_handle().unwrap();
|
||||
if let Err(e) = app_handle.global_shortcut().unregister_all() {
|
||||
log::error!(target:"app", "Error unregistering all hotkeys: {:?}", e);
|
||||
logging!(
|
||||
error,
|
||||
Type::Hotkey,
|
||||
true,
|
||||
"Error unregistering all hotkeys: {:?}",
|
||||
e
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,17 @@
|
||||
use crate::{config::Config, utils::dirs};
|
||||
use crate::{
|
||||
config::Config,
|
||||
logging,
|
||||
utils::{dirs, logging::Type},
|
||||
};
|
||||
use anyhow::{bail, Context, Result};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{collections::HashMap, env::current_exe, path::PathBuf, process::Command as StdCommand, time::{SystemTime, UNIX_EPOCH}};
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
env::current_exe,
|
||||
path::PathBuf,
|
||||
process::Command as StdCommand,
|
||||
time::{SystemTime, UNIX_EPOCH},
|
||||
};
|
||||
use tokio::time::Duration;
|
||||
|
||||
// Windows only
|
||||
@@ -11,15 +21,15 @@ const REQUIRED_SERVICE_VERSION: &str = "1.0.5"; // 定义所需的服务版本
|
||||
|
||||
// 限制重装时间和次数的常量
|
||||
const REINSTALL_COOLDOWN_SECS: u64 = 300; // 5分钟冷却期
|
||||
const MAX_REINSTALLS_PER_DAY: u32 = 3; // 每24小时最多重装3次
|
||||
const ONE_DAY_SECS: u64 = 86400; // 24小时的秒数
|
||||
const MAX_REINSTALLS_PER_DAY: u32 = 3; // 每24小时最多重装3次
|
||||
const ONE_DAY_SECS: u64 = 86400; // 24小时的秒数
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, Clone, Default)]
|
||||
pub struct ServiceState {
|
||||
pub last_install_time: u64, // 上次安装时间戳 (Unix 时间戳,秒)
|
||||
pub install_count: u32, // 24小时内安装次数
|
||||
pub last_check_time: u64, // 上次检查时间
|
||||
pub last_error: Option<String>, // 上次错误信息
|
||||
pub last_install_time: u64, // 上次安装时间戳 (Unix 时间戳,秒)
|
||||
pub install_count: u32, // 24小时内安装次数
|
||||
pub last_check_time: u64, // 上次检查时间
|
||||
pub last_error: Option<String>, // 上次错误信息
|
||||
}
|
||||
|
||||
impl ServiceState {
|
||||
@@ -47,12 +57,12 @@ impl ServiceState {
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.unwrap_or_default()
|
||||
.as_secs();
|
||||
|
||||
|
||||
// 检查是否需要重置计数器(24小时已过)
|
||||
if now - self.last_install_time > ONE_DAY_SECS {
|
||||
self.install_count = 0;
|
||||
}
|
||||
|
||||
|
||||
self.last_install_time = now;
|
||||
self.install_count += 1;
|
||||
}
|
||||
@@ -63,17 +73,19 @@ impl ServiceState {
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.unwrap_or_default()
|
||||
.as_secs();
|
||||
|
||||
|
||||
// 如果在冷却期内,不允许重装
|
||||
if now - self.last_install_time < REINSTALL_COOLDOWN_SECS {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// 如果24小时内安装次数过多,也不允许
|
||||
if now - self.last_install_time < ONE_DAY_SECS && self.install_count >= MAX_REINSTALLS_PER_DAY {
|
||||
if now - self.last_install_time < ONE_DAY_SECS
|
||||
&& self.install_count >= MAX_REINSTALLS_PER_DAY
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
true
|
||||
}
|
||||
}
|
||||
@@ -112,7 +124,7 @@ pub async fn reinstall_service() -> Result<()> {
|
||||
|
||||
// 获取当前服务状态
|
||||
let mut service_state = ServiceState::get();
|
||||
|
||||
|
||||
// 检查是否允许重装
|
||||
if !service_state.can_reinstall() {
|
||||
log::warn!(target:"app", "service reinstall rejected: cooldown period or max attempts reached");
|
||||
@@ -216,6 +228,12 @@ pub async fn reinstall_service() -> Result<()> {
|
||||
);
|
||||
}
|
||||
|
||||
// 记录安装信息并保存
|
||||
let mut service_state = ServiceState::get();
|
||||
service_state.record_install();
|
||||
service_state.last_error = None;
|
||||
service_state.save()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -268,6 +286,13 @@ pub async fn reinstall_service() -> Result<()> {
|
||||
status.code().unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
// 记录安装信息并保存
|
||||
let mut service_state = ServiceState::get();
|
||||
service_state.record_install();
|
||||
service_state.last_error = None;
|
||||
service_state.save()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -314,7 +339,7 @@ pub async fn check_service_version() -> Result<String> {
|
||||
pub async fn check_service_needs_reinstall() -> bool {
|
||||
// 获取当前服务状态
|
||||
let service_state = ServiceState::get();
|
||||
|
||||
|
||||
// 首先检查是否在冷却期或超过重装次数限制
|
||||
if !service_state.can_reinstall() {
|
||||
log::info!(target: "app", "service reinstall check: in cooldown period or max attempts reached");
|
||||
@@ -326,21 +351,21 @@ pub async fn check_service_needs_reinstall() -> bool {
|
||||
Ok(version) => {
|
||||
// 打印更详细的日志,方便排查问题
|
||||
log::info!(target: "app", "服务版本检测:当前={}, 要求={}", version, REQUIRED_SERVICE_VERSION);
|
||||
|
||||
|
||||
let needs_reinstall = version != REQUIRED_SERVICE_VERSION;
|
||||
if needs_reinstall {
|
||||
log::warn!(target: "app", "发现服务版本不匹配,需要重装! 当前={}, 要求={}",
|
||||
log::warn!(target: "app", "发现服务版本不匹配,需要重装! 当前={}, 要求={}",
|
||||
version, REQUIRED_SERVICE_VERSION);
|
||||
|
||||
|
||||
// 打印版本字符串的原始字节,确认没有隐藏字符
|
||||
log::debug!(target: "app", "当前版本字节: {:?}", version.as_bytes());
|
||||
log::debug!(target: "app", "要求版本字节: {:?}", REQUIRED_SERVICE_VERSION.as_bytes());
|
||||
} else {
|
||||
log::info!(target: "app", "服务版本匹配,无需重装");
|
||||
}
|
||||
|
||||
|
||||
needs_reinstall
|
||||
},
|
||||
}
|
||||
Err(err) => {
|
||||
// 检查服务是否可用,如果可用但版本检查失败,可能只是版本API有问题
|
||||
match is_service_running().await {
|
||||
@@ -408,9 +433,9 @@ pub(super) async fn run_core_by_service(config_file: &PathBuf) -> Result<()> {
|
||||
// 先检查服务版本,不受冷却期限制
|
||||
let version_check = match check_service_version().await {
|
||||
Ok(version) => {
|
||||
log::info!(target: "app", "检测到服务版本: {}, 要求版本: {}",
|
||||
log::info!(target: "app", "检测到服务版本: {}, 要求版本: {}",
|
||||
version, REQUIRED_SERVICE_VERSION);
|
||||
|
||||
|
||||
// 通过字节比较确保完全匹配
|
||||
if version.as_bytes() != REQUIRED_SERVICE_VERSION.as_bytes() {
|
||||
log::warn!(target: "app", "服务版本不匹配,需要重装");
|
||||
@@ -419,7 +444,7 @@ pub(super) async fn run_core_by_service(config_file: &PathBuf) -> Result<()> {
|
||||
log::info!(target: "app", "服务版本匹配");
|
||||
true // 版本匹配
|
||||
}
|
||||
},
|
||||
}
|
||||
Err(err) => {
|
||||
log::warn!(target: "app", "无法获取服务版本: {}", err);
|
||||
false // 无法获取版本
|
||||
@@ -434,11 +459,11 @@ pub(super) async fn run_core_by_service(config_file: &PathBuf) -> Result<()> {
|
||||
return start_with_existing_service(config_file).await;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 强制执行版本检查,如果版本不匹配则重装
|
||||
if !version_check {
|
||||
log::info!(target: "app", "服务版本不匹配,尝试重装");
|
||||
|
||||
|
||||
// 获取服务状态,检查是否可以重装
|
||||
let service_state = ServiceState::get();
|
||||
if !service_state.can_reinstall() {
|
||||
@@ -451,22 +476,22 @@ pub(super) async fn run_core_by_service(config_file: &PathBuf) -> Result<()> {
|
||||
bail!("服务版本不匹配且无法重装,启动失败");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 尝试重装
|
||||
log::info!(target: "app", "开始重装服务");
|
||||
if let Err(err) = reinstall_service().await {
|
||||
log::warn!(target: "app", "服务重装失败: {}", err);
|
||||
|
||||
|
||||
// 尝试使用现有服务
|
||||
log::info!(target: "app", "尝试使用现有服务");
|
||||
return start_with_existing_service(config_file).await;
|
||||
}
|
||||
|
||||
|
||||
// 重装成功,尝试启动
|
||||
log::info!(target: "app", "服务重装成功,尝试启动");
|
||||
return start_with_existing_service(config_file).await;
|
||||
}
|
||||
|
||||
|
||||
// 检查服务状态
|
||||
match check_service().await {
|
||||
Ok(_) => {
|
||||
@@ -475,22 +500,22 @@ pub(super) async fn run_core_by_service(config_file: &PathBuf) -> Result<()> {
|
||||
if let Ok(()) = start_with_existing_service(config_file).await {
|
||||
return Ok(());
|
||||
}
|
||||
},
|
||||
}
|
||||
Err(err) => {
|
||||
log::warn!(target: "app", "服务检查失败: {}", err);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 服务不可用或启动失败,检查是否需要重装
|
||||
if check_service_needs_reinstall().await {
|
||||
log::info!(target: "app", "服务需要重装");
|
||||
|
||||
|
||||
// 尝试重装
|
||||
if let Err(err) = reinstall_service().await {
|
||||
log::warn!(target: "app", "服务重装失败: {}", err);
|
||||
bail!("Failed to reinstall service: {}", err);
|
||||
}
|
||||
|
||||
|
||||
// 重装后再次尝试启动
|
||||
log::info!(target: "app", "服务重装完成,尝试启动核心");
|
||||
start_with_existing_service(config_file).await
|
||||
@@ -521,12 +546,22 @@ pub async fn is_service_running() -> Result<bool> {
|
||||
|
||||
// 检查服务状态码和消息
|
||||
if resp.code == 0 && resp.msg == "ok" && resp.data.is_some() {
|
||||
logging!(debug, Type::Service, "Service is running");
|
||||
Ok(true)
|
||||
} else {
|
||||
logging!(debug, Type::Service, "Service is not running");
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn is_service_available() -> Result<()> {
|
||||
let resp = check_service().await?;
|
||||
if resp.code == 0 && resp.msg == "ok" && resp.data.is_some() {
|
||||
logging!(debug, Type::Service, "Service is available");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 强制重装服务(用于UI中的修复服务按钮)
|
||||
pub async fn force_reinstall_service() -> Result<()> {
|
||||
log::info!(target: "app", "用户请求强制重装服务");
|
||||
@@ -534,15 +569,15 @@ pub async fn force_reinstall_service() -> Result<()> {
|
||||
// 创建默认服务状态(重置所有限制)
|
||||
let service_state = ServiceState::default();
|
||||
service_state.save()?;
|
||||
|
||||
|
||||
log::info!(target: "app", "已重置服务状态,开始执行重装");
|
||||
|
||||
|
||||
// 执行重装
|
||||
match reinstall_service().await {
|
||||
Ok(()) => {
|
||||
log::info!(target: "app", "服务重装成功");
|
||||
Ok(())
|
||||
},
|
||||
}
|
||||
Err(err) => {
|
||||
log::error!(target: "app", "强制重装服务失败: {}", err);
|
||||
bail!("强制重装服务失败: {}", err)
|
||||
|
||||
@@ -47,8 +47,6 @@ pub fn change_clash_mode(mode: String) {
|
||||
});
|
||||
tauri::async_runtime::spawn(async move {
|
||||
log::debug!(target: "app", "change clash mode to {mode}");
|
||||
CoreManager::global().ensure_running_core().await;
|
||||
|
||||
match MihomoManager::global().patch_configs(json_value).await {
|
||||
Ok(_) => {
|
||||
// 更新订阅
|
||||
@@ -67,7 +65,6 @@ pub fn change_clash_mode(mode: String) {
|
||||
|
||||
/// Test connection delay to a URL
|
||||
pub async fn test_delay(url: String) -> anyhow::Result<u32> {
|
||||
CoreManager::global().ensure_running_core().await;
|
||||
use tokio::time::{Duration, Instant};
|
||||
let mut builder = reqwest::ClientBuilder::new().use_rustls_tls().no_proxy();
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ impl PlatformSpecification {
|
||||
tokio::runtime::Handle::current().block_on(async {
|
||||
match CoreManager::global().get_running_mode().await {
|
||||
crate::core::RunningMode::Service => "Service".to_string(),
|
||||
crate::core::RunningMode::Sidecar => "Sidecar".to_string(),
|
||||
crate::core::RunningMode::Sidecar => "Standalone".to_string(),
|
||||
crate::core::RunningMode::NotRunning => "Not Running".to_string(),
|
||||
}
|
||||
})
|
||||
|
||||
@@ -136,52 +136,6 @@ pub fn linux_elevator() -> String {
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! error {
|
||||
($result: expr) => {
|
||||
log::error!(target: "app", "{}", $result);
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! log_err {
|
||||
($result: expr) => {
|
||||
if let Err(err) = $result {
|
||||
log::error!(target: "app", "{err}");
|
||||
}
|
||||
};
|
||||
|
||||
($result: expr, $err_str: expr) => {
|
||||
if let Err(_) = $result {
|
||||
log::error!(target: "app", "{}", $err_str);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! trace_err {
|
||||
($result: expr, $err_str: expr) => {
|
||||
if let Err(err) = $result {
|
||||
log::trace!(target: "app", "{}, err {}", $err_str, err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// wrap the anyhow error
|
||||
/// transform the error to String
|
||||
#[macro_export]
|
||||
macro_rules! wrap_err {
|
||||
($stat: expr) => {
|
||||
match $stat {
|
||||
Ok(a) => Ok(a),
|
||||
Err(err) => {
|
||||
log::error!(target: "app", "{}", err.to_string());
|
||||
Err(format!("{}", err.to_string()))
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// return the string literal error
|
||||
#[macro_export]
|
||||
macro_rules! ret_err {
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum Type {
|
||||
Core,
|
||||
Service,
|
||||
Hotkey,
|
||||
Window,
|
||||
}
|
||||
|
||||
impl fmt::Display for Type {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Type::Core => write!(f, "[Core]"),
|
||||
Type::Service => write!(f, "[Service]"),
|
||||
Type::Hotkey => write!(f, "[Hotkey]"),
|
||||
Type::Window => write!(f, "[Window]"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! error {
|
||||
($result: expr) => {
|
||||
log::error!(target: "app", "{}", $result);
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! log_err {
|
||||
($result: expr) => {
|
||||
if let Err(err) = $result {
|
||||
log::error!(target: "app", "{err}");
|
||||
}
|
||||
};
|
||||
|
||||
($result: expr, $err_str: expr) => {
|
||||
if let Err(_) = $result {
|
||||
log::error!(target: "app", "{}", $err_str);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! trace_err {
|
||||
($result: expr, $err_str: expr) => {
|
||||
if let Err(err) = $result {
|
||||
log::trace!(target: "app", "{}, err {}", $err_str, err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// wrap the anyhow error
|
||||
/// transform the error to String
|
||||
#[macro_export]
|
||||
macro_rules! wrap_err {
|
||||
($stat: expr) => {
|
||||
match $stat {
|
||||
Ok(a) => Ok(a),
|
||||
Err(err) => {
|
||||
log::error!(target: "app", "{}", err.to_string());
|
||||
Err(format!("{}", err.to_string()))
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! logging {
|
||||
// 带 println 的版本(支持格式化参数)
|
||||
($level:ident, $type:expr, $print:expr, $($arg:tt)*) => {
|
||||
println!("{} {}", $type, format_args!($($arg)*));
|
||||
log::$level!(target: "app", "{} {}", $type, format_args!($($arg)*));
|
||||
};
|
||||
|
||||
// 不带 println 的版本
|
||||
($level:ident, $type:expr, $($arg:tt)*) => {
|
||||
log::$level!(target: "app", "{} {}", $type, format_args!($($arg)*));
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! logging_error {
|
||||
// Version with println and Result expression
|
||||
($type:expr, $print:expr, $expr:expr) => {
|
||||
match $expr {
|
||||
Ok(_) => {},
|
||||
Err(err) => {
|
||||
if $print {
|
||||
println!("[{}] Error: {}", $type, err);
|
||||
}
|
||||
log::error!(target: "app", "{} {}", $type, err);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Version without println and Result expression
|
||||
($type:expr, $expr:expr) => {
|
||||
if let Err(err) = $expr {
|
||||
log::error!(target: "app", "{} {}", $type, err);
|
||||
}
|
||||
};
|
||||
|
||||
// Version with println and custom message
|
||||
($type:expr, $print:expr, $($arg:tt)*) => {
|
||||
if $print {
|
||||
println!("[{}] {}", $type, format_args!($($arg)*));
|
||||
}
|
||||
log::error!(target: "app", "{} {}", $type, format_args!($($arg)*));
|
||||
};
|
||||
|
||||
// Version without println and custom message
|
||||
($type:expr, $($arg:tt)*) => {
|
||||
log::error!(target: "app", "{} {}", $type, format_args!($($arg)*));
|
||||
};
|
||||
}
|
||||
@@ -3,6 +3,7 @@ pub mod error;
|
||||
pub mod help;
|
||||
pub mod i18n;
|
||||
pub mod init;
|
||||
pub mod logging;
|
||||
pub mod resolve;
|
||||
pub mod server;
|
||||
pub mod tmpl;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"version": "2.2.1",
|
||||
"version": "2.2.2",
|
||||
"$schema": "../node_modules/@tauri-apps/cli/config.schema.json",
|
||||
"bundle": {
|
||||
"active": true,
|
||||
@@ -30,6 +30,14 @@
|
||||
"plugins": {
|
||||
"updater": {
|
||||
"pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IEQyOEMyRjBCQkVGOUJEREYKUldUZnZmbStDeStNMHU5Mmo1N24xQXZwSVRYbXA2NUpzZE5oVzlqeS9Bc0t6RVV4MmtwVjBZaHgK",
|
||||
"endpoints": [
|
||||
"https://download.clashverge.dev/https://github.com/clash-verge-rev/clash-verge-rev/releases/download/updater/update-proxy.json",
|
||||
"https://gh-proxy.com/https://github.com/clash-verge-rev/clash-verge-rev/releases/download/updater/update-proxy.json",
|
||||
"https://github.com/clash-verge-rev/clash-verge-rev/releases/download/updater/update.json",
|
||||
"https://download.clashverge.dev/https://github.com/clash-verge-rev/clash-verge-rev/releases/download/updater-alpha/update-alpha-proxy.json",
|
||||
"https://gh-proxy.com/https://github.com/clash-verge-rev/clash-verge-rev/releases/download/updater-alpha/update-alpha-proxy.json",
|
||||
"https://github.com/clash-verge-rev/clash-verge-rev/releases/download/updater-alpha/update-alpha.json"
|
||||
],
|
||||
"windows": {
|
||||
"installMode": "basicUi"
|
||||
}
|
||||
|
||||
@@ -353,7 +353,6 @@
|
||||
"clash_mode_direct": "直连模式",
|
||||
"toggle_system_proxy": "打开/关闭系统代理",
|
||||
"toggle_tun_mode": "打开/关闭 TUN 模式",
|
||||
"toggle_lightweight_mode": "进入轻量模式",
|
||||
"entry_lightweight_mode": "进入轻量模式",
|
||||
"Backup Setting": "备份设置",
|
||||
"Backup Setting Info": "支持 WebDAV 备份配置文件",
|
||||
|
||||
@@ -249,8 +249,8 @@ const Layout = () => {
|
||||
? {
|
||||
borderRadius: "8px",
|
||||
border: "1px solid var(--divider-color)",
|
||||
width: "calc(100vw - 0px)",
|
||||
height: "calc(100vh - 0px)",
|
||||
width: "calc(100vw - 4px)",
|
||||
height: "calc(100vh - 4px)",
|
||||
}
|
||||
: {},
|
||||
]}
|
||||
|
||||
Reference in New Issue
Block a user