mirror of
https://github.com/Melorise/cp-nix.git
synced 2026-11-07 14:39:34 +00:00
init
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
result
|
||||
result-*
|
||||
@@ -0,0 +1,39 @@
|
||||
# Nix 打包说明
|
||||
|
||||
本目录把锁定版本的上游桌面项目从源码构建为 Linux Nix Flake,不复用上游预制安装包。界面代码、原生 Node 模块和最终 ASAR 均在 Nix 构建中生成;共享 Electron、稳定运行组件及部分随包资源来自 nixpkgs 或 Flake inputs。版本和来源由 `flake.lock`、pnpm 锁文件及 Cargo 锁文件记录。
|
||||
|
||||
## 补丁说明
|
||||
|
||||
补丁按 `nix/unwrapped.nix` 中的顺序应用。
|
||||
|
||||
### 标识调整
|
||||
|
||||
上游构建配置中的可执行文件名、桌面项和图标标识与本地包名不同。补丁统一这些标识,避免生成物保留不匹配的窗口类、图标名和可执行文件元数据。
|
||||
|
||||
### 禁用内置更新
|
||||
|
||||
Nix store 只读,运行中的程序不能可靠替换自身或随包运行组件;绕过 Flake 锁定下载更新还会产生版本混用。补丁因此在 Linux 上跳过应用更新检查,并拒绝运行组件的内置升级请求,更新统一通过重新构建 Flake 完成。
|
||||
|
||||
### 固定稳定运行组件
|
||||
|
||||
当前打包只提供 nixpkgs 中的稳定版本,没有随包放入其他发布通道的文件。补丁让 Linux 路径解析始终选择已提供的稳定文件,防止旧配置或界面状态指向不存在的替代文件。
|
||||
|
||||
### NixOS 权限包装
|
||||
|
||||
上游会检查 setuid 所有权,并尝试通过 `pkexec` 对程序文件执行 `chown` 和 `chmod`;这些操作不适用于只读 Nix store。NixOS 模块改为在 `/run/wrappers` 创建 capability 包装器。补丁在需要额外权限时选择该包装器,并把它的存在视为权限已经配置;未启用模块时则给出明确错误,而不尝试修改 store。
|
||||
|
||||
### 共享 Electron 兼容
|
||||
|
||||
Nix 启动器使用共享 Electron,并把 `app.asar` 作为参数传入。此方式下 Electron 将进程视为 default app,`app.isPackaged` 为 `false`;而工具库把 `is.dev` 定义为 `!app.isPackaged`。若直接沿用该判断,正式构建会误走开发分支,自动打开 DevTools、选择开发资源目录,并影响静默启动和窗口页面加载。
|
||||
|
||||
补丁把“工具库判断为开发环境且应用路径不是 ASAR”作为实际开发状态。另一个差异是共享 Electron 的 `process.resourcesPath` 指向 Electron 自身的 store 路径,而不是应用资源目录;补丁改从 `app.getAppPath()` 所指 ASAR 的父目录定位随包文件和原生模块。主窗口、悬浮窗口、资源复制和原生模块加载共用这一约定,删除其中任一处修改都可能重新造成路径不一致。
|
||||
|
||||
## NixOS 模块
|
||||
|
||||
模块负责安装软件包和创建上述权限包装器,并提供可选的图形会话自启动及额外 Electron 参数。它不创建常驻系统服务。
|
||||
|
||||
默认构建:
|
||||
|
||||
```sh
|
||||
nix build
|
||||
```
|
||||
Generated
+128
@@ -0,0 +1,128 @@
|
||||
{
|
||||
"nodes": {
|
||||
"asn": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"narHash": "sha256-bYBRUboYCyEixK/jdhPayyKdB0HQegLmiFXvzB7ZFDU=",
|
||||
"type": "file",
|
||||
"url": "https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/GeoLite2-ASN.mmdb"
|
||||
},
|
||||
"original": {
|
||||
"type": "file",
|
||||
"url": "https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/GeoLite2-ASN.mmdb"
|
||||
}
|
||||
},
|
||||
"clash-party": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1783321592,
|
||||
"narHash": "sha256-ituoVv/q/aHt3KOt4fHRDGHGEwnF/nb2E73s/oYw74U=",
|
||||
"owner": "mihomo-party-org",
|
||||
"repo": "clash-party",
|
||||
"rev": "1caff4cabd83d0e2768ecadf52c85c5dcc0ed9de",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "mihomo-party-org",
|
||||
"ref": "v2.0.0",
|
||||
"repo": "clash-party",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"country": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"narHash": "sha256-DpM+/Jsyy7R51/AQKDBVhuT8Fgr1crpz0uaX/0xV6bs=",
|
||||
"type": "file",
|
||||
"url": "https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/country-lite.mmdb"
|
||||
},
|
||||
"original": {
|
||||
"type": "file",
|
||||
"url": "https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/country-lite.mmdb"
|
||||
}
|
||||
},
|
||||
"geoip": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"narHash": "sha256-ov9zEg2tBCpxru5uNzMs6Npaawz0CXthUaBDQIBcOqs=",
|
||||
"type": "file",
|
||||
"url": "https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geoip.dat"
|
||||
},
|
||||
"original": {
|
||||
"type": "file",
|
||||
"url": "https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geoip.dat"
|
||||
}
|
||||
},
|
||||
"geosite": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"narHash": "sha256-PAA0zUq0to9HpJ7+sXXcCm6GgvuZvci5HVoH/u41m0k=",
|
||||
"type": "file",
|
||||
"url": "https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geosite.dat"
|
||||
},
|
||||
"original": {
|
||||
"type": "file",
|
||||
"url": "https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geosite.dat"
|
||||
}
|
||||
},
|
||||
"metadb": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"narHash": "sha256-UVm3qjQmgmDSzv6zXPgVXfEQsHwDzl3yTWrRXzh9hrs=",
|
||||
"type": "file",
|
||||
"url": "https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geoip.metadb"
|
||||
},
|
||||
"original": {
|
||||
"type": "file",
|
||||
"url": "https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geoip.metadb"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1784796856,
|
||||
"narHash": "sha256-wWFrV5/Qbm+lyt5x20E/bSbfJiGKMo4RCxZV8cl/WZI=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "e2587caef70cea85dd97d7daab492899902dbf5d",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"asn": "asn",
|
||||
"clash-party": "clash-party",
|
||||
"country": "country",
|
||||
"geoip": "geoip",
|
||||
"geosite": "geosite",
|
||||
"metadb": "metadb",
|
||||
"nixpkgs": "nixpkgs",
|
||||
"sysproxy": "sysproxy"
|
||||
}
|
||||
},
|
||||
"sysproxy": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1781896006,
|
||||
"narHash": "sha256-z/IlR6kQfzFkg42fmKKTUTFGgcMLobcNZHeHQFDN5kw=",
|
||||
"owner": "mihomo-party-org",
|
||||
"repo": "sysproxy-rs-opti",
|
||||
"rev": "d6fbb3dee696739e7d32ece15479a7770f687c9b",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "mihomo-party-org",
|
||||
"ref": "main",
|
||||
"repo": "sysproxy-rs-opti",
|
||||
"type": "github"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
{
|
||||
description = "Clash Party, built from source with a reproducible Nix flake";
|
||||
|
||||
inputs = {
|
||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||
|
||||
clash-party = {
|
||||
url = "github:mihomo-party-org/clash-party/v2.0.0";
|
||||
flake = false;
|
||||
};
|
||||
|
||||
sysproxy = {
|
||||
url = "github:mihomo-party-org/sysproxy-rs-opti/main";
|
||||
flake = false;
|
||||
};
|
||||
|
||||
country = {
|
||||
url = "file+https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/country-lite.mmdb";
|
||||
flake = false;
|
||||
};
|
||||
|
||||
geoip = {
|
||||
url = "file+https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geoip.dat";
|
||||
flake = false;
|
||||
};
|
||||
|
||||
geosite = {
|
||||
url = "file+https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geosite.dat";
|
||||
flake = false;
|
||||
};
|
||||
|
||||
metadb = {
|
||||
url = "file+https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geoip.metadb";
|
||||
flake = false;
|
||||
};
|
||||
|
||||
asn = {
|
||||
url = "file+https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/GeoLite2-ASN.mmdb";
|
||||
flake = false;
|
||||
};
|
||||
};
|
||||
|
||||
outputs =
|
||||
inputs@{ self, nixpkgs, ... }:
|
||||
let
|
||||
systems = [
|
||||
"x86_64-linux"
|
||||
"aarch64-linux"
|
||||
];
|
||||
forEachSystem = nixpkgs.lib.genAttrs systems;
|
||||
mkPkgs =
|
||||
system:
|
||||
import nixpkgs {
|
||||
inherit system;
|
||||
config.allowUnfree = true;
|
||||
};
|
||||
mkPackage =
|
||||
system:
|
||||
let
|
||||
pkgs = mkPkgs system;
|
||||
in
|
||||
pkgs.callPackage ./nix/package.nix {
|
||||
src = inputs.clash-party;
|
||||
sysproxySrc = inputs.sysproxy;
|
||||
inherit (inputs)
|
||||
country
|
||||
geoip
|
||||
geosite
|
||||
metadb
|
||||
asn
|
||||
;
|
||||
subStore = pkgs.sub-store;
|
||||
subStoreFrontend = pkgs.sub-store-frontend;
|
||||
notoFontsColorEmoji = pkgs.noto-fonts-color-emoji;
|
||||
};
|
||||
in
|
||||
{
|
||||
packages = forEachSystem (
|
||||
system:
|
||||
let
|
||||
package = mkPackage system;
|
||||
in
|
||||
{
|
||||
clash-party = package;
|
||||
default = package;
|
||||
inherit (package) sysproxy unwrapped;
|
||||
}
|
||||
);
|
||||
|
||||
apps = forEachSystem (system: {
|
||||
clash-party = {
|
||||
type = "app";
|
||||
program = "${self.packages.${system}.clash-party}/bin/clash-party";
|
||||
};
|
||||
default = self.apps.${system}.clash-party;
|
||||
});
|
||||
|
||||
nixosModules = {
|
||||
clash-party = import ./nix/module.nix { inherit self; };
|
||||
default = self.nixosModules.clash-party;
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
{ self }:
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
cfg = config.programs.clash-party;
|
||||
system = pkgs.stdenv.hostPlatform.system;
|
||||
package = cfg.package.override {
|
||||
commandLineArgs = cfg.extraArgs;
|
||||
};
|
||||
in
|
||||
{
|
||||
options.programs.clash-party = {
|
||||
enable = lib.mkEnableOption "Clash Party";
|
||||
|
||||
package = lib.mkOption {
|
||||
type = lib.types.package;
|
||||
default = self.packages.${system}.clash-party;
|
||||
defaultText = lib.literalExpression "clash-party-flake.packages.${system}.clash-party";
|
||||
description = "Clash Party package to install.";
|
||||
};
|
||||
|
||||
autoStart = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = false;
|
||||
description = "Start Clash Party automatically in graphical sessions.";
|
||||
};
|
||||
|
||||
extraArgs = lib.mkOption {
|
||||
type = lib.types.listOf lib.types.str;
|
||||
default = [ ];
|
||||
example = [ "--ozone-platform-hint=auto" ];
|
||||
description = "Additional command-line arguments passed to Electron.";
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
environment.systemPackages = [ package ];
|
||||
|
||||
security.wrappers.clash-party-mihomo = {
|
||||
source = lib.getExe package.mihomo;
|
||||
owner = "root";
|
||||
group = "root";
|
||||
capabilities = "cap_net_admin,cap_net_raw,cap_net_bind_service=+ep";
|
||||
};
|
||||
|
||||
environment.etc."xdg/autostart/clash-party.desktop" = lib.mkIf cfg.autoStart {
|
||||
source = "${package}/share/applications/clash-party.desktop";
|
||||
};
|
||||
};
|
||||
}
|
||||
+132
@@ -0,0 +1,132 @@
|
||||
{
|
||||
lib,
|
||||
callPackage,
|
||||
stdenv,
|
||||
electron_41,
|
||||
makeWrapper,
|
||||
copyDesktopItems,
|
||||
makeDesktopItem,
|
||||
wrapGAppsHook3,
|
||||
autoPatchelfHook,
|
||||
alsa-lib,
|
||||
at-spi2-core,
|
||||
glib,
|
||||
libGL,
|
||||
libayatana-appindicator,
|
||||
libsecret,
|
||||
libnotify,
|
||||
libXScrnSaver,
|
||||
libXtst,
|
||||
nspr,
|
||||
nss,
|
||||
openssl,
|
||||
udev,
|
||||
src,
|
||||
sysproxySrc,
|
||||
country,
|
||||
geoip,
|
||||
geosite,
|
||||
metadb,
|
||||
asn,
|
||||
subStore,
|
||||
subStoreFrontend,
|
||||
notoFontsColorEmoji,
|
||||
mihomo,
|
||||
commandLineArgs ? [ ],
|
||||
}:
|
||||
|
||||
let
|
||||
sysproxy = callPackage ./sysproxy.nix { inherit sysproxySrc; };
|
||||
|
||||
unwrapped = callPackage ./unwrapped.nix {
|
||||
inherit
|
||||
src
|
||||
sysproxy
|
||||
country
|
||||
geoip
|
||||
geosite
|
||||
metadb
|
||||
asn
|
||||
subStore
|
||||
subStoreFrontend
|
||||
notoFontsColorEmoji
|
||||
mihomo
|
||||
;
|
||||
};
|
||||
in
|
||||
stdenv.mkDerivation (finalAttrs: {
|
||||
pname = "clash-party";
|
||||
version = "2.0.0";
|
||||
|
||||
dontUnpack = true;
|
||||
|
||||
nativeBuildInputs = [
|
||||
autoPatchelfHook
|
||||
copyDesktopItems
|
||||
makeWrapper
|
||||
wrapGAppsHook3
|
||||
];
|
||||
|
||||
buildInputs = [
|
||||
alsa-lib
|
||||
at-spi2-core
|
||||
glib
|
||||
libGL
|
||||
libayatana-appindicator
|
||||
libnotify
|
||||
libsecret
|
||||
libXScrnSaver
|
||||
libXtst
|
||||
nspr
|
||||
nss
|
||||
openssl
|
||||
stdenv.cc.cc.lib
|
||||
udev
|
||||
];
|
||||
|
||||
installPhase = ''
|
||||
runHook preInstall
|
||||
|
||||
mkdir -p $out/lib/clash-party
|
||||
cp -r ${unwrapped}/lib/clash-party/resources $out/lib/clash-party/
|
||||
cp -r ${unwrapped}/share $out/
|
||||
chmod -R u+w $out/share
|
||||
|
||||
makeWrapper ${lib.getExe electron_41} $out/bin/clash-party \
|
||||
--inherit-argv0 \
|
||||
--add-flags "$out/lib/clash-party/resources/app.asar" \
|
||||
--add-flags ${lib.escapeShellArg (lib.escapeShellArgs commandLineArgs)} \
|
||||
--set-default ELECTRON_OZONE_PLATFORM_HINT auto
|
||||
|
||||
runHook postInstall
|
||||
'';
|
||||
|
||||
desktopItems = [
|
||||
(makeDesktopItem {
|
||||
name = "clash-party";
|
||||
desktopName = "Clash Party";
|
||||
genericName = "Proxy Client";
|
||||
comment = "A GUI client based on Mihomo";
|
||||
exec = "clash-party %U";
|
||||
icon = "clash-party";
|
||||
startupWMClass = "clash-party";
|
||||
categories = [ "Utility" ];
|
||||
mimeTypes = [
|
||||
"x-scheme-handler/clash"
|
||||
"x-scheme-handler/mihomo"
|
||||
];
|
||||
})
|
||||
];
|
||||
|
||||
passthru = {
|
||||
inherit mihomo sysproxy unwrapped;
|
||||
};
|
||||
|
||||
meta = {
|
||||
description = "Clash Party desktop client built from source";
|
||||
homepage = "https://github.com/mihomo-party-org/clash-party";
|
||||
license = lib.licenses.gpl3Only;
|
||||
mainProgram = "clash-party";
|
||||
platforms = lib.platforms.linux;
|
||||
};
|
||||
})
|
||||
Generated
+1155
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,60 @@
|
||||
{
|
||||
lib,
|
||||
rustPlatform,
|
||||
fetchPnpmDeps,
|
||||
nodejs_22,
|
||||
pnpm_10,
|
||||
pnpmConfigHook,
|
||||
sysproxySrc,
|
||||
}:
|
||||
|
||||
rustPlatform.buildRustPackage (finalAttrs: {
|
||||
pname = "clash-party-sysproxy";
|
||||
version = "0.5.1";
|
||||
src = sysproxySrc;
|
||||
|
||||
cargoLock.lockFile = ./sysproxy-Cargo.lock;
|
||||
|
||||
pnpmDeps = fetchPnpmDeps {
|
||||
pname = finalAttrs.pname;
|
||||
version = finalAttrs.version;
|
||||
src = sysproxySrc;
|
||||
pnpm = pnpm_10;
|
||||
fetcherVersion = 4;
|
||||
prePnpmInstall = ''
|
||||
pnpm config set fetch-retries 5
|
||||
pnpm config set fetch-timeout 120000
|
||||
pnpm config set network-concurrency 4
|
||||
'';
|
||||
hash = "sha256-n/unBCkpSSShYliEeeWOAjM7r7ZUdfROBYyqzw8JLB0=";
|
||||
};
|
||||
|
||||
nativeBuildInputs = [
|
||||
nodejs_22
|
||||
pnpmConfigHook
|
||||
pnpm_10
|
||||
];
|
||||
|
||||
postPatch = ''
|
||||
rm -f .cargo/config.toml
|
||||
cp ${./sysproxy-Cargo.lock} Cargo.lock
|
||||
chmod u+w Cargo.lock
|
||||
'';
|
||||
|
||||
buildPhase = ''
|
||||
runHook preBuild
|
||||
pnpm exec napi build --platform --release
|
||||
runHook postBuild
|
||||
'';
|
||||
|
||||
# N-API symbols are supplied by Node/Electron when the module is loaded, so
|
||||
# Cargo's standalone test-link step cannot resolve them.
|
||||
doCheck = false;
|
||||
|
||||
installPhase = ''
|
||||
runHook preInstall
|
||||
mkdir -p $out
|
||||
cp sysproxy.*.node $out/
|
||||
runHook postInstall
|
||||
'';
|
||||
})
|
||||
@@ -0,0 +1,139 @@
|
||||
{
|
||||
lib,
|
||||
stdenv,
|
||||
autoPatchelfHook,
|
||||
electron_41,
|
||||
fetchPnpmDeps,
|
||||
nodejs_22,
|
||||
pnpm_10,
|
||||
pnpmConfigHook,
|
||||
alsa-lib,
|
||||
at-spi2-core,
|
||||
glib,
|
||||
libGL,
|
||||
libayatana-appindicator,
|
||||
libnotify,
|
||||
libsecret,
|
||||
libXScrnSaver,
|
||||
libXtst,
|
||||
nspr,
|
||||
nss,
|
||||
openssl,
|
||||
pkg-config,
|
||||
udev,
|
||||
src,
|
||||
sysproxy,
|
||||
country,
|
||||
geoip,
|
||||
geosite,
|
||||
metadb,
|
||||
asn,
|
||||
subStore,
|
||||
subStoreFrontend,
|
||||
notoFontsColorEmoji,
|
||||
mihomo,
|
||||
}:
|
||||
|
||||
stdenv.mkDerivation (finalAttrs: {
|
||||
pname = "clash-party-unwrapped";
|
||||
version = "2.0.0";
|
||||
inherit src;
|
||||
|
||||
patches = [
|
||||
../patches/branding.patch
|
||||
../patches/disable-updater.patch
|
||||
../patches/stable-core-only.patch
|
||||
../patches/nix-tun-wrapper.patch
|
||||
../patches/system-electron-resources.patch
|
||||
];
|
||||
|
||||
pnpmDeps = fetchPnpmDeps {
|
||||
pname = finalAttrs.pname;
|
||||
version = finalAttrs.version;
|
||||
inherit src;
|
||||
pnpm = pnpm_10;
|
||||
fetcherVersion = 4;
|
||||
prePnpmInstall = ''
|
||||
pnpm config set fetch-retries 5
|
||||
pnpm config set fetch-timeout 120000
|
||||
pnpm config set network-concurrency 4
|
||||
'';
|
||||
hash = "sha256-plsb9TjmprG5k2kYkqItEunTeyJWopRBH0SzzU/qM/8=";
|
||||
};
|
||||
|
||||
nativeBuildInputs = [
|
||||
autoPatchelfHook
|
||||
nodejs_22
|
||||
pkg-config
|
||||
pnpmConfigHook
|
||||
pnpm_10
|
||||
];
|
||||
|
||||
buildInputs = [
|
||||
alsa-lib
|
||||
at-spi2-core
|
||||
glib
|
||||
libGL
|
||||
libayatana-appindicator
|
||||
libnotify
|
||||
libsecret
|
||||
libXScrnSaver
|
||||
libXtst
|
||||
nspr
|
||||
nss
|
||||
openssl
|
||||
stdenv.cc.cc.lib
|
||||
udev
|
||||
];
|
||||
|
||||
env = {
|
||||
ELECTRON_SKIP_BINARY_DOWNLOAD = "1";
|
||||
npm_config_build_from_source = "true";
|
||||
};
|
||||
|
||||
postPatch = ''
|
||||
mkdir -p extra/sidecar extra/files
|
||||
ln -s ${mihomo}/bin/mihomo extra/sidecar/mihomo
|
||||
cp ${sysproxy}/sysproxy.linux-${if stdenv.hostPlatform.isx86_64 then "x64" else "arm64"}-gnu.node \
|
||||
extra/sidecar/
|
||||
|
||||
install -Dm444 ${country} extra/files/country.mmdb
|
||||
install -Dm444 ${geoip} extra/files/geoip.dat
|
||||
install -Dm444 ${geosite} extra/files/geosite.dat
|
||||
install -Dm444 ${metadb} extra/files/geoip.metadb
|
||||
install -Dm444 ${asn} extra/files/ASN.mmdb
|
||||
install -Dm444 ${subStore}/share/sub-store/sub-store.bundle.js \
|
||||
extra/files/sub-store.bundle.cjs
|
||||
cp -r ${subStoreFrontend} extra/files/sub-store-frontend
|
||||
find ${notoFontsColorEmoji} -name NotoColorEmoji.ttf -exec \
|
||||
install -Dm444 {} src/renderer/src/assets/NotoColorEmoji.ttf \;
|
||||
'';
|
||||
|
||||
buildPhase = ''
|
||||
runHook preBuild
|
||||
|
||||
mkdir .electron-dist
|
||||
cp -RL ${electron_41.dist}/. .electron-dist/
|
||||
chmod -R u+w .electron-dist
|
||||
|
||||
node_modules/.bin/electron-vite build
|
||||
npm_config_nodedir=${electron_41.headers} \
|
||||
node_modules/.bin/electron-builder --dir \
|
||||
--config=electron-builder.yml \
|
||||
--config.electronDist=$PWD/.electron-dist \
|
||||
--config.electronVersion=${electron_41.version}
|
||||
|
||||
runHook postBuild
|
||||
'';
|
||||
|
||||
installPhase = ''
|
||||
runHook preInstall
|
||||
|
||||
mkdir -p $out/lib/clash-party
|
||||
cp -r dist/linux-unpacked/resources $out/lib/clash-party/
|
||||
install -Dm644 build/icon.png $out/share/icons/hicolor/512x512/apps/clash-party.png
|
||||
|
||||
runHook postInstall
|
||||
'';
|
||||
|
||||
})
|
||||
@@ -0,0 +1,11 @@
|
||||
diff --git a/electron-builder.yml b/electron-builder.yml
|
||||
--- a/electron-builder.yml
|
||||
+++ b/electron-builder.yml
|
||||
@@ -59 +59 @@
|
||||
- executableName: mihomo-party
|
||||
+ executableName: clash-party
|
||||
@@ -68,2 +68,2 @@
|
||||
- StartupWMClass: mihomo-party
|
||||
- Icon: mihomo-party
|
||||
+ StartupWMClass: clash-party
|
||||
+ Icon: clash-party
|
||||
@@ -0,0 +1,24 @@
|
||||
diff --git a/src/main/resolve/autoUpdater.ts b/src/main/resolve/autoUpdater.ts
|
||||
--- a/src/main/resolve/autoUpdater.ts
|
||||
+++ b/src/main/resolve/autoUpdater.ts
|
||||
@@ -48,4 +48,8 @@
|
||||
export async function checkUpdate(): Promise<IAppVersion | undefined> {
|
||||
+ if (process.platform === 'linux') {
|
||||
+ return undefined
|
||||
+ }
|
||||
+
|
||||
const [{ 'mixed-port': mixedPort = DEFAULT_MIHOMO_PORTS.mixed }, { githubProxy = '' }] =
|
||||
await Promise.all([getControledMihomoConfig(), getAppConfig()])
|
||||
const githubUrl =
|
||||
diff --git a/src/main/core/mihomoApi.ts b/src/main/core/mihomoApi.ts
|
||||
--- a/src/main/core/mihomoApi.ts
|
||||
+++ b/src/main/core/mihomoApi.ts
|
||||
@@ -400,4 +400,8 @@
|
||||
export const mihomoUpgrade = async (): Promise<void> => {
|
||||
+ if (process.platform === 'linux') {
|
||||
+ throw new Error('Mihomo is managed by Nix on Linux')
|
||||
+ }
|
||||
+
|
||||
const instance = await getAxios()
|
||||
return await instance.post('/upgrade', undefined, { timeout: 90000 })
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
diff --git a/src/main/core/manager.ts b/src/main/core/manager.ts
|
||||
--- a/src/main/core/manager.ts
|
||||
+++ b/src/main/core/manager.ts
|
||||
@@ -79,3 +79,4 @@
|
||||
const execFilePromise = promisify(execFile)
|
||||
const ctlParam = process.platform === 'win32' ? '-ext-ctl-pipe' : '-ext-ctl-unix'
|
||||
+const nixMihomoWrapper = '/run/wrappers/bin/clash-party-mihomo'
|
||||
const coreHookTimeout = 30000
|
||||
@@ -370,5 +371,8 @@
|
||||
return {
|
||||
- corePath: mihomoCorePath(core),
|
||||
+ corePath:
|
||||
+ process.platform === 'linux' && tun?.enable && existsSync(nixMihomoWrapper)
|
||||
+ ? nixMihomoWrapper
|
||||
+ : mihomoCorePath(core),
|
||||
workDir: diffWorkDir ? mihomoProfileWorkDir(current) : mihomoWorkDir(),
|
||||
safePath: diffWorkDir ? mihomoWorkDir() : undefined,
|
||||
ipcPath,
|
||||
diff --git a/src/main/core/permissions.ts b/src/main/core/permissions.ts
|
||||
--- a/src/main/core/permissions.ts
|
||||
+++ b/src/main/core/permissions.ts
|
||||
@@ -14,5 +14,6 @@
|
||||
const execPromise = promisify(exec)
|
||||
const execFilePromise = promisify(execFile)
|
||||
+const nixMihomoWrapper = '/run/wrappers/bin/clash-party-mihomo'
|
||||
|
||||
// 内核名称白名单
|
||||
const ALLOWED_CORES = ['mihomo', 'mihomo-alpha', 'mihomo-smart'] as const
|
||||
@@ -76,9 +77,13 @@
|
||||
try {
|
||||
if (process.platform === 'win32') {
|
||||
return await checkAdminPrivileges()
|
||||
}
|
||||
|
||||
+ if (process.platform === 'linux' && existsSync(nixMihomoWrapper)) {
|
||||
+ return true
|
||||
+ }
|
||||
+
|
||||
if (process.platform === 'darwin' || process.platform === 'linux') {
|
||||
const stats = await stat(corePath)
|
||||
return (stats.mode & 0o4000) !== 0 && stats.uid === 0
|
||||
}
|
||||
@@ -253,4 +258,9 @@
|
||||
if (process.platform === 'linux') {
|
||||
- await execFilePromise('pkexec', ['chown', 'root:root', corePath])
|
||||
- await execFilePromise('pkexec', ['chmod', '+sx', corePath])
|
||||
+ if (existsSync(nixMihomoWrapper)) {
|
||||
+ return
|
||||
+ }
|
||||
+
|
||||
+ throw new Error(
|
||||
+ 'TUN support requires enabling programs.clash-party in the NixOS configuration'
|
||||
+ )
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
diff --git a/src/main/utils/dirs.ts b/src/main/utils/dirs.ts
|
||||
--- a/src/main/utils/dirs.ts
|
||||
+++ b/src/main/utils/dirs.ts
|
||||
@@ -79,6 +79,10 @@
|
||||
export function mihomoCorePath(core: string): string {
|
||||
+ if (process.platform === 'linux') {
|
||||
+ core = 'mihomo'
|
||||
+ }
|
||||
+
|
||||
const isWin = process.platform === 'win32'
|
||||
// 处理 Smart 内核
|
||||
if (core === 'mihomo-smart') {
|
||||
@@ -0,0 +1,107 @@
|
||||
diff --git a/src/native/sysproxy/index.js b/src/native/sysproxy/index.js
|
||||
--- a/src/native/sysproxy/index.js
|
||||
+++ b/src/native/sysproxy/index.js
|
||||
@@ -70,6 +70,12 @@ function getResourcesPath() {
|
||||
try {
|
||||
const { app } = require('electron')
|
||||
const appPath = app.getAppPath()
|
||||
+ // A shared system Electron keeps process.resourcesPath in Electron's own
|
||||
+ // store path. The directory containing app.asar is the app resource path.
|
||||
+ if (appPath.endsWith('.asar') && existsSync(join(dirname(appPath), 'sidecar'))) {
|
||||
+ return dirname(appPath)
|
||||
+ }
|
||||
+
|
||||
if (existsSync(join(appPath, 'extra', 'sidecar'))) {
|
||||
return appPath
|
||||
}
|
||||
diff --git a/src/main/utils/dirs.ts b/src/main/utils/dirs.ts
|
||||
--- a/src/main/utils/dirs.ts
|
||||
+++ b/src/main/utils/dirs.ts
|
||||
@@ -62,13 +62,16 @@ export function workDir(): string {
|
||||
}
|
||||
|
||||
+export function isDevelopment(): boolean {
|
||||
+ return is.dev && !app.getAppPath().endsWith('asar')
|
||||
+}
|
||||
+
|
||||
export function resourcesDir(): string {
|
||||
- if (is.dev) {
|
||||
+ const appPath = app.getAppPath()
|
||||
+ if (appPath.endsWith('asar')) {
|
||||
+ return path.dirname(appPath)
|
||||
+ } else if (isDevelopment()) {
|
||||
return path.join(__dirname, '../../extra')
|
||||
} else {
|
||||
- if (app.getAppPath().endsWith('asar')) {
|
||||
- return process.resourcesPath
|
||||
- } else {
|
||||
- return path.join(app.getAppPath(), 'resources')
|
||||
- }
|
||||
+ return path.join(appPath, 'resources')
|
||||
}
|
||||
}
|
||||
diff --git a/src/main/window.ts b/src/main/window.ts
|
||||
--- a/src/main/window.ts
|
||||
+++ b/src/main/window.ts
|
||||
@@ -1,11 +1,10 @@
|
||||
import { join } from 'path'
|
||||
import { readFileSync, writeFileSync } from 'fs'
|
||||
import { BrowserWindow, Menu, screen, shell } from 'electron'
|
||||
-import { is } from '@electron-toolkit/utils'
|
||||
import icon from '../../resources/icon.png?asset'
|
||||
import { getAppConfig } from './config'
|
||||
import { quitWithoutCore, stopCore } from './core/manager'
|
||||
import { triggerSysProxy } from './sys/sysproxy'
|
||||
import { hideDockIcon, showDockIcon } from './resolve/tray'
|
||||
-import { dataDir } from './utils/dirs'
|
||||
+import { dataDir, isDevelopment } from './utils/dirs'
|
||||
import { mainWindowLogger } from './utils/logger'
|
||||
@@ -172,10 +171,10 @@ export async function createMainWindow(silentStart = false): Promise<void> {
|
||||
})
|
||||
|
||||
- if (is.dev) {
|
||||
+ if (isDevelopment()) {
|
||||
mainWindow.webContents.openDevTools()
|
||||
}
|
||||
|
||||
- if (is.dev && process.env['ELECTRON_RENDERER_URL']) {
|
||||
+ if (isDevelopment() && process.env['ELECTRON_RENDERER_URL']) {
|
||||
mainWindow.loadURL(process.env['ELECTRON_RENDERER_URL'])
|
||||
} else {
|
||||
mainWindow.loadFile(join(__dirname, '../renderer/index.html'))
|
||||
@@ -199,7 +198,7 @@ function setupWindowEvents(window: BrowserWindow, config: WindowConfig): void {
|
||||
}
|
||||
|
||||
// 开发模式下始终显示窗口
|
||||
- if (!silentStart || is.dev) {
|
||||
+ if (!silentStart || isDevelopment()) {
|
||||
clearQuitTimeout()
|
||||
window.show()
|
||||
window.focusOnWebView()
|
||||
diff --git a/src/main/resolve/floatingWindow.ts b/src/main/resolve/floatingWindow.ts
|
||||
--- a/src/main/resolve/floatingWindow.ts
|
||||
+++ b/src/main/resolve/floatingWindow.ts
|
||||
@@ -1,8 +1,8 @@
|
||||
import { join } from 'path'
|
||||
-import { is } from '@electron-toolkit/utils'
|
||||
import { BrowserWindow, ipcMain } from 'electron'
|
||||
import windowStateKeeper from 'electron-window-state'
|
||||
import { getAppConfig, patchAppConfig } from '../config'
|
||||
+import { isDevelopment } from '../utils/dirs'
|
||||
import { floatingWindowLogger } from '../utils/logger'
|
||||
import { applyTheme } from './theme'
|
||||
import { buildContextMenu, showTrayIcon } from './tray'
|
||||
@@ -86,11 +86,11 @@ async function createFloatingWindow(): Promise<void> {
|
||||
|
||||
// 加载页面
|
||||
const url =
|
||||
- is.dev && process.env['ELECTRON_RENDERER_URL']
|
||||
+ isDevelopment() && process.env['ELECTRON_RENDERER_URL']
|
||||
? `${process.env['ELECTRON_RENDERER_URL']}/floating.html`
|
||||
: join(__dirname, '../renderer/floating.html')
|
||||
|
||||
- is.dev ? await floatingWindow.loadURL(url) : await floatingWindow.loadFile(url)
|
||||
+ isDevelopment() ? await floatingWindow.loadURL(url) : await floatingWindow.loadFile(url)
|
||||
} catch (error) {
|
||||
logError('Failed to create floating window', error)
|
||||
floatingWindow = null
|
||||
Reference in New Issue
Block a user