mirror of
https://github.com/snltty/linker.git
synced 2025-10-24 01:23:17 +08:00
修复虚拟网卡添加路由错误,和增加自动更新
This commit is contained in:
14
.github/workflows/dotnet.yml
vendored
14
.github/workflows/dotnet.yml
vendored
@@ -48,16 +48,18 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: '${{ secrets.ACTIONS_TOKEN }}'
|
GITHUB_TOKEN: '${{ secrets.ACTIONS_TOKEN }}'
|
||||||
with:
|
with:
|
||||||
tag_name: v1.1.1.3
|
tag_name: v1.1.2.1
|
||||||
release_name: v1.1.1.3.${{ steps.date.outputs.today }}
|
release_name: v1.1.2.1.${{ steps.date.outputs.today }}
|
||||||
draft: false
|
draft: false
|
||||||
prerelease: false
|
prerelease: false
|
||||||
body: |
|
body: |
|
||||||
1. 更新配置同步方式,以版本同步
|
1. 更新配置同步方式,以版本同步
|
||||||
2. 增加设备列表搜索,按 设备名/设备IP/虚拟网卡IP/端口转发端口和IP 搜索
|
2. 设备列表搜索,按 设备名/设备IP/虚拟网卡IP/端口转发端口和IP 搜索
|
||||||
3. 新增端口转发配置复制,可以将A转发到B的配置复制给C转发到B
|
3. 端口转发配置复制,可以将A转发到B的配置复制给C转发到B
|
||||||
4. 新增服务器代理穿透配置复制
|
4. 服务器代理穿透配置复制
|
||||||
5. 请更新服务端
|
5. 修复虚拟网卡添加路由错误
|
||||||
|
6. 自动更新,管理所有客户端更新
|
||||||
|
7. 请更新服务端
|
||||||
|
|
||||||
- name: upload win x64
|
- name: upload win x64
|
||||||
id: upload-win-x64
|
id: upload-win-x64
|
||||||
|
4
.github/workflows/nuget.yml
vendored
4
.github/workflows/nuget.yml
vendored
@@ -36,5 +36,5 @@ jobs:
|
|||||||
|
|
||||||
- name: Push
|
- name: Push
|
||||||
run: |
|
run: |
|
||||||
nuget push ./linker.tunnel/bin/release/linker.tunnel.1.1.1.nupkg -Source https://api.nuget.org/v3/index.json -SkipDuplicate -ApiKey ${{ secrets.NUGET_KEY }} -NoSymbol
|
nuget push ./linker.tunnel/bin/release/linker.tunnel.1.1.2.nupkg -Source https://api.nuget.org/v3/index.json -SkipDuplicate -ApiKey ${{ secrets.NUGET_KEY }} -NoSymbol
|
||||||
nuget push ./linker.libs/bin/release/linker.libs.1.1.1.nupkg -Source https://api.nuget.org/v3/index.json -SkipDuplicate -ApiKey ${{ secrets.NUGET_KEY }} -NoSymbol
|
nuget push ./linker.libs/bin/release/linker.libs.1.1.2.nupkg -Source https://api.nuget.org/v3/index.json -SkipDuplicate -ApiKey ${{ secrets.NUGET_KEY }} -NoSymbol
|
||||||
|
@@ -1,53 +0,0 @@
|
|||||||
version: '1.0'
|
|
||||||
name: branch-pipeline
|
|
||||||
displayName: BranchPipeline
|
|
||||||
stages:
|
|
||||||
- stage:
|
|
||||||
name: compile
|
|
||||||
displayName: 编译
|
|
||||||
steps:
|
|
||||||
- step: build@maven
|
|
||||||
name: build_maven
|
|
||||||
displayName: Maven 构建
|
|
||||||
# 支持6、7、8、9、10、11六个版本
|
|
||||||
jdkVersion: 8
|
|
||||||
# 支持2.2.1、3.2.5、3.3.9、3.5.2、3.5.3、3.5.4、3.6.1、3.6.3八个版本
|
|
||||||
mavenVersion: 3.3.9
|
|
||||||
# 构建命令
|
|
||||||
commands:
|
|
||||||
- mvn -B clean package -Dmaven.test.skip=true
|
|
||||||
# 非必填字段,开启后表示将构建产物暂存,但不会上传到制品库中,7天后自动清除
|
|
||||||
artifacts:
|
|
||||||
# 构建产物名字,作为产物的唯一标识可向下传递,支持自定义,默认为BUILD_ARTIFACT。在下游可以通过${BUILD_ARTIFACT}方式引用来获取构建物地址
|
|
||||||
- name: BUILD_ARTIFACT
|
|
||||||
# 构建产物获取路径,是指代码编译完毕之后构建物的所在路径,如通常jar包在target目录下。当前目录为代码库根目录
|
|
||||||
path:
|
|
||||||
- ./target
|
|
||||||
- step: publish@general_artifacts
|
|
||||||
name: publish_general_artifacts
|
|
||||||
displayName: 上传制品
|
|
||||||
# 上游构建任务定义的产物名,默认BUILD_ARTIFACT
|
|
||||||
dependArtifact: BUILD_ARTIFACT
|
|
||||||
# 上传到制品库时的制品命名,默认output
|
|
||||||
artifactName: output
|
|
||||||
dependsOn: build_maven
|
|
||||||
- stage:
|
|
||||||
name: release
|
|
||||||
displayName: 发布
|
|
||||||
steps:
|
|
||||||
- step: publish@release_artifacts
|
|
||||||
name: publish_release_artifacts
|
|
||||||
displayName: '发布'
|
|
||||||
# 上游上传制品任务的产出
|
|
||||||
dependArtifact: output
|
|
||||||
# 发布制品版本号
|
|
||||||
version: '1.0.0.0'
|
|
||||||
# 是否开启版本号自增,默认开启
|
|
||||||
autoIncrement: true
|
|
||||||
triggers:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
exclude:
|
|
||||||
- master
|
|
||||||
include:
|
|
||||||
- .*
|
|
@@ -1,51 +0,0 @@
|
|||||||
version: '1.0'
|
|
||||||
name: master-pipeline
|
|
||||||
displayName: MasterPipeline
|
|
||||||
stages:
|
|
||||||
- stage:
|
|
||||||
name: compile
|
|
||||||
displayName: 编译
|
|
||||||
steps:
|
|
||||||
- step: build@maven
|
|
||||||
name: build_maven
|
|
||||||
displayName: Maven 构建
|
|
||||||
# 支持6、7、8、9、10、11六个版本
|
|
||||||
jdkVersion: 8
|
|
||||||
# 支持2.2.1、3.2.5、3.3.9、3.5.2、3.5.3、3.5.4、3.6.1、3.6.3八个版本
|
|
||||||
mavenVersion: 3.3.9
|
|
||||||
# 构建命令
|
|
||||||
commands:
|
|
||||||
- mvn -B clean package -Dmaven.test.skip=true
|
|
||||||
# 非必填字段,开启后表示将构建产物暂存,但不会上传到制品库中,7天后自动清除
|
|
||||||
artifacts:
|
|
||||||
# 构建产物名字,作为产物的唯一标识可向下传递,支持自定义,默认为BUILD_ARTIFACT。在下游可以通过${BUILD_ARTIFACT}方式引用来获取构建物地址
|
|
||||||
- name: BUILD_ARTIFACT
|
|
||||||
# 构建产物获取路径,是指代码编译完毕之后构建物的所在路径,如通常jar包在target目录下。当前目录为代码库根目录
|
|
||||||
path:
|
|
||||||
- ./target
|
|
||||||
- step: publish@general_artifacts
|
|
||||||
name: publish_general_artifacts
|
|
||||||
displayName: 上传制品
|
|
||||||
# 上游构建任务定义的产物名,默认BUILD_ARTIFACT
|
|
||||||
dependArtifact: BUILD_ARTIFACT
|
|
||||||
# 上传到制品库时的制品命名,默认output
|
|
||||||
artifactName: output
|
|
||||||
dependsOn: build_maven
|
|
||||||
- stage:
|
|
||||||
name: release
|
|
||||||
displayName: 发布
|
|
||||||
steps:
|
|
||||||
- step: publish@release_artifacts
|
|
||||||
name: publish_release_artifacts
|
|
||||||
displayName: '发布'
|
|
||||||
# 上游上传制品任务的产出
|
|
||||||
dependArtifact: output
|
|
||||||
# 发布制品版本号
|
|
||||||
version: '1.0.0.0'
|
|
||||||
# 是否开启版本号自增,默认开启
|
|
||||||
autoIncrement: true
|
|
||||||
triggers:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
include:
|
|
||||||
- master
|
|
@@ -1,40 +0,0 @@
|
|||||||
version: '1.0'
|
|
||||||
name: pr-pipeline
|
|
||||||
displayName: PRPipeline
|
|
||||||
stages:
|
|
||||||
- stage:
|
|
||||||
name: compile
|
|
||||||
displayName: 编译
|
|
||||||
steps:
|
|
||||||
- step: build@maven
|
|
||||||
name: build_maven
|
|
||||||
displayName: Maven 构建
|
|
||||||
# 支持6、7、8、9、10、11六个版本
|
|
||||||
jdkVersion: 8
|
|
||||||
# 支持2.2.1、3.2.5、3.3.9、3.5.2、3.5.3、3.5.4、3.6.1、3.6.3八个版本
|
|
||||||
mavenVersion: 3.3.9
|
|
||||||
# 构建命令
|
|
||||||
commands:
|
|
||||||
- mvn -B clean package -Dmaven.test.skip=true
|
|
||||||
# 非必填字段,开启后表示将构建产物暂存,但不会上传到制品库中,7天后自动清除
|
|
||||||
artifacts:
|
|
||||||
# 构建产物名字,作为产物的唯一标识可向下传递,支持自定义,默认为BUILD_ARTIFACT。在下游可以通过${BUILD_ARTIFACT}方式引用来获取构建物地址
|
|
||||||
- name: BUILD_ARTIFACT
|
|
||||||
# 构建产物获取路径,是指代码编译完毕之后构建物的所在路径,如通常jar包在target目录下。当前目录为代码库根目录
|
|
||||||
path:
|
|
||||||
- ./target
|
|
||||||
- step: publish@general_artifacts
|
|
||||||
name: publish_general_artifacts
|
|
||||||
displayName: 上传制品
|
|
||||||
# 上游构建任务定义的产物名,默认BUILD_ARTIFACT
|
|
||||||
dependArtifact: BUILD_ARTIFACT
|
|
||||||
# 构建产物制品库,默认default,系统默认创建
|
|
||||||
artifactRepository: default
|
|
||||||
# 上传到制品库时的制品命名,默认output
|
|
||||||
artifactName: output
|
|
||||||
dependsOn: build_maven
|
|
||||||
triggers:
|
|
||||||
pr:
|
|
||||||
branches:
|
|
||||||
include:
|
|
||||||
- master
|
|
13
linker.doc.web/docs/6、发布.md
Normal file
13
linker.doc.web/docs/6、发布.md
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
---
|
||||||
|
sidebar_position: 6
|
||||||
|
---
|
||||||
|
|
||||||
|
# 6、发布
|
||||||
|
|
||||||
|
你可以自己发布项目,因为涉及到很多内容,所以建议使用脚本发布
|
||||||
|
|
||||||
|
1. 安装 <a href="https://nodejs.org/en/download/package-manager">Nodejs https://nodejs.org/en/download/package-manager</a>
|
||||||
|
2. 安装 <a href="https://dotnet.microsoft.com/zh-cn/download">.NET8.0 SDK https://dotnet.microsoft.com/zh-cn/download</a> 或者安装 <a href="https://visualstudio.microsoft.com/zh-hans/vs/">vs2022 https://visualstudio.microsoft.com/zh-hans/vs/</a>
|
||||||
|
3. 发布项目自动压缩,所以你需要安装 <a href="https://www.7-zip.org/">7zip https://www.7-zip.org/</a>
|
||||||
|
4. 在`cmd`或者`PowerShell`下运行根目录下`publish.bat`,等待发布完成
|
||||||
|
5. 发布完成后,在根目录下,`public`>`publish` 和 `public`>`publish-zip`
|
@@ -1,8 +1,8 @@
|
|||||||
---
|
---
|
||||||
sidebar_position: 6
|
sidebar_position: 7
|
||||||
---
|
---
|
||||||
|
|
||||||
# 6、集成打洞到你的项目
|
# 7、集成打洞到你的项目
|
||||||
|
|
||||||
你需要自己实现信标服务器,用于交换打洞信息
|
你需要自己实现信标服务器,用于交换打洞信息
|
||||||
|
|
@@ -14,9 +14,9 @@
|
|||||||
<Copyright>snltty</Copyright>
|
<Copyright>snltty</Copyright>
|
||||||
<PackageProjectUrl>https://github.com/snltty/linker</PackageProjectUrl>
|
<PackageProjectUrl>https://github.com/snltty/linker</PackageProjectUrl>
|
||||||
<RepositoryUrl>https://github.com/snltty/linker</RepositoryUrl>
|
<RepositoryUrl>https://github.com/snltty/linker</RepositoryUrl>
|
||||||
<Version>1.1.1</Version>
|
<Version>1.1.2</Version>
|
||||||
<AssemblyVersion>1.1.1.3</AssemblyVersion>
|
<AssemblyVersion>1.1.2.1</AssemblyVersion>
|
||||||
<FileVersion>1.1.1.3</FileVersion>
|
<FileVersion>1.1.2.1</FileVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||||
<DebugType>full</DebugType>
|
<DebugType>full</DebugType>
|
||||||
|
@@ -14,7 +14,7 @@
|
|||||||
<PublishAot>false</PublishAot>
|
<PublishAot>false</PublishAot>
|
||||||
<JsonSerializerIsReflectionEnabledByDefault>true</JsonSerializerIsReflectionEnabledByDefault>
|
<JsonSerializerIsReflectionEnabledByDefault>true</JsonSerializerIsReflectionEnabledByDefault>
|
||||||
<EnablePreviewFeatures>true</EnablePreviewFeatures>
|
<EnablePreviewFeatures>true</EnablePreviewFeatures>
|
||||||
<Version>1.1.1</Version>
|
<Version>1.1.2</Version>
|
||||||
<Authors>snltty</Authors>
|
<Authors>snltty</Authors>
|
||||||
<Company>snltty</Company>
|
<Company>snltty</Company>
|
||||||
<Description>snltty</Description>
|
<Description>snltty</Description>
|
||||||
@@ -22,8 +22,8 @@
|
|||||||
<PackageProjectUrl>https://github.com/snltty/linker</PackageProjectUrl>
|
<PackageProjectUrl>https://github.com/snltty/linker</PackageProjectUrl>
|
||||||
<RepositoryUrl>https://github.com/snltty/linker</RepositoryUrl>
|
<RepositoryUrl>https://github.com/snltty/linker</RepositoryUrl>
|
||||||
<PackageReleaseNotes>snltty service</PackageReleaseNotes>
|
<PackageReleaseNotes>snltty service</PackageReleaseNotes>
|
||||||
<AssemblyVersion>1.1.1.3</AssemblyVersion>
|
<AssemblyVersion>1.1.2.1</AssemblyVersion>
|
||||||
<FileVersion>1.1.1.3</FileVersion>
|
<FileVersion>1.1.2.1</FileVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||||
|
14
linker.sln
14
linker.sln
@@ -13,8 +13,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "linker.service", "linker.se
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "linker.tunnel", "linker.tunnel\linker.tunnel.csproj", "{AFADE8D6-AB00-456B-9F43-53BC95B7B608}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "linker.tunnel", "linker.tunnel\linker.tunnel.csproj", "{AFADE8D6-AB00-456B-9F43-53BC95B7B608}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "linker.updater", "linker.updater\linker.updater.csproj", "{B7E2B873-C96E-4F3E-9411-D8D549F4D3A5}"
|
|
||||||
EndProject
|
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
@@ -85,18 +83,6 @@ Global
|
|||||||
{AFADE8D6-AB00-456B-9F43-53BC95B7B608}.Release|x64.Build.0 = Release|Any CPU
|
{AFADE8D6-AB00-456B-9F43-53BC95B7B608}.Release|x64.Build.0 = Release|Any CPU
|
||||||
{AFADE8D6-AB00-456B-9F43-53BC95B7B608}.Release|x86.ActiveCfg = Release|Any CPU
|
{AFADE8D6-AB00-456B-9F43-53BC95B7B608}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
{AFADE8D6-AB00-456B-9F43-53BC95B7B608}.Release|x86.Build.0 = Release|Any CPU
|
{AFADE8D6-AB00-456B-9F43-53BC95B7B608}.Release|x86.Build.0 = Release|Any CPU
|
||||||
{B7E2B873-C96E-4F3E-9411-D8D549F4D3A5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{B7E2B873-C96E-4F3E-9411-D8D549F4D3A5}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{B7E2B873-C96E-4F3E-9411-D8D549F4D3A5}.Debug|x64.ActiveCfg = Debug|Any CPU
|
|
||||||
{B7E2B873-C96E-4F3E-9411-D8D549F4D3A5}.Debug|x64.Build.0 = Debug|Any CPU
|
|
||||||
{B7E2B873-C96E-4F3E-9411-D8D549F4D3A5}.Debug|x86.ActiveCfg = Debug|Any CPU
|
|
||||||
{B7E2B873-C96E-4F3E-9411-D8D549F4D3A5}.Debug|x86.Build.0 = Debug|Any CPU
|
|
||||||
{B7E2B873-C96E-4F3E-9411-D8D549F4D3A5}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{B7E2B873-C96E-4F3E-9411-D8D549F4D3A5}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
{B7E2B873-C96E-4F3E-9411-D8D549F4D3A5}.Release|x64.ActiveCfg = Release|Any CPU
|
|
||||||
{B7E2B873-C96E-4F3E-9411-D8D549F4D3A5}.Release|x64.Build.0 = Release|Any CPU
|
|
||||||
{B7E2B873-C96E-4F3E-9411-D8D549F4D3A5}.Release|x86.ActiveCfg = Release|Any CPU
|
|
||||||
{B7E2B873-C96E-4F3E-9411-D8D549F4D3A5}.Release|x86.Build.0 = Release|Any CPU
|
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
@@ -9,7 +9,7 @@
|
|||||||
<JsonSerializerIsReflectionEnabledByDefault>true</JsonSerializerIsReflectionEnabledByDefault>
|
<JsonSerializerIsReflectionEnabledByDefault>true</JsonSerializerIsReflectionEnabledByDefault>
|
||||||
<EnablePreviewFeatures>true</EnablePreviewFeatures>
|
<EnablePreviewFeatures>true</EnablePreviewFeatures>
|
||||||
<Title>linker tunnel</Title>
|
<Title>linker tunnel</Title>
|
||||||
<Version>1.1.1</Version>
|
<Version>1.1.2</Version>
|
||||||
<Authors>snltty</Authors>
|
<Authors>snltty</Authors>
|
||||||
<Company>snltty</Company>
|
<Company>snltty</Company>
|
||||||
<Description>linker tunnel</Description>
|
<Description>linker tunnel</Description>
|
||||||
@@ -17,8 +17,8 @@
|
|||||||
<PackageProjectUrl>https://github.com/snltty/linker</PackageProjectUrl>
|
<PackageProjectUrl>https://github.com/snltty/linker</PackageProjectUrl>
|
||||||
<RepositoryUrl>https://github.com/snltty/linker</RepositoryUrl>
|
<RepositoryUrl>https://github.com/snltty/linker</RepositoryUrl>
|
||||||
<PackageReleaseNotes>linker tunnel</PackageReleaseNotes>
|
<PackageReleaseNotes>linker tunnel</PackageReleaseNotes>
|
||||||
<AssemblyVersion>1.1.1.3</AssemblyVersion>
|
<AssemblyVersion>1.1.2.1</AssemblyVersion>
|
||||||
<FileVersion>1.1.1.3</FileVersion>
|
<FileVersion>1.1.2.1</FileVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||||
|
@@ -1,256 +0,0 @@
|
|||||||
using Fizzler.Systems.HtmlAgilityPack;
|
|
||||||
using HtmlAgilityPack;
|
|
||||||
using linker.libs;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.IO.Compression;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace linker.updater
|
|
||||||
{
|
|
||||||
internal class Program
|
|
||||||
{
|
|
||||||
static async Task Main(string[] args)
|
|
||||||
{
|
|
||||||
if (args.Length > 0)
|
|
||||||
{
|
|
||||||
rootPath = args[0];
|
|
||||||
}
|
|
||||||
Updater();
|
|
||||||
await Helper.Await();
|
|
||||||
}
|
|
||||||
|
|
||||||
static string rootPath = "./updater";
|
|
||||||
static void Updater()
|
|
||||||
{
|
|
||||||
Task.Factory.StartNew(async () =>
|
|
||||||
{
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
UpdateInfo updateInfo = GetUpdateInfo();
|
|
||||||
if (updateInfo != null)
|
|
||||||
{
|
|
||||||
if (NeedDownload())
|
|
||||||
{
|
|
||||||
await DownloadUpdate(updateInfo);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (NeedExtract())
|
|
||||||
{
|
|
||||||
ExtractUpdate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
await Task.Delay(15000);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}, TaskCreationOptions.LongRunning);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool NeedExtract()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return File.Exists(Path.Join(rootPath, "updater.zip")) && File.Exists(Path.Join(rootPath, "extract.txt"));
|
|
||||||
}
|
|
||||||
catch (Exception)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
static void ExtractUpdate()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
string[] command = File.ReadAllText(Path.Join(rootPath, "extract.txt")).Split(Environment.NewLine);
|
|
||||||
CommandHelper.Execute(string.Empty, new string[] { command[0] });
|
|
||||||
|
|
||||||
ZipFile.ExtractToDirectory(Path.Join(rootPath, "updater.zip"), Path.Join(rootPath, "../"), Encoding.UTF8, true);
|
|
||||||
|
|
||||||
File.Delete(Path.Join(rootPath, "extract.txt"));
|
|
||||||
File.Delete(Path.Join(rootPath, "updater.zip"));
|
|
||||||
|
|
||||||
CommandHelper.Execute(string.Empty, new string[] { command[1] });
|
|
||||||
}
|
|
||||||
catch (Exception)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool NeedDownload()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return File.Exists(Path.Join(rootPath, "extract.txt"));
|
|
||||||
}
|
|
||||||
catch (Exception)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
static async Task DownloadUpdate(UpdateInfo updateInfo)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (Directory.Exists(rootPath) == false)
|
|
||||||
{
|
|
||||||
Directory.CreateDirectory(rootPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
using FileStream fileStream = new FileStream(Path.Join(rootPath, "updater.zip"), FileMode.OpenOrCreate, FileAccess.ReadWrite);
|
|
||||||
using HttpClient httpClient = new HttpClient();
|
|
||||||
using Stream stream = await httpClient.GetStreamAsync(updateInfo.Url);
|
|
||||||
await stream.CopyToAsync(fileStream);
|
|
||||||
|
|
||||||
fileStream.Flush();
|
|
||||||
fileStream.Close();
|
|
||||||
fileStream.Dispose();
|
|
||||||
}
|
|
||||||
catch (Exception)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static UpdateInfo GetUpdateInfo()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
using HttpClient httpClient = new HttpClient();
|
|
||||||
string str = httpClient.GetStringAsync("http://gh.snltty.com:1808/https://github.com/snltty/linker/releases/latest").Result;
|
|
||||||
HtmlDocument hdc = new HtmlDocument();
|
|
||||||
hdc.LoadHtml(str);
|
|
||||||
string tag = hdc.DocumentNode.QuerySelector("span.css-truncate-target span").InnerText.Trim();
|
|
||||||
|
|
||||||
str = httpClient.GetStringAsync($"http://gh.snltty.com:1808/https://github.com/snltty/linker/releases/expanded_assets/{tag}").Result;
|
|
||||||
HtmlDocument hdc1 = new HtmlDocument();
|
|
||||||
hdc1.LoadHtml(str);
|
|
||||||
string msg = hdc.DocumentNode.QuerySelector(".markdown-body").InnerText.Trim();
|
|
||||||
|
|
||||||
string system = OperatingSystem.IsWindows() ? "win" : OperatingSystem.IsLinux() ? "linux" : "osx";
|
|
||||||
string arch = RuntimeInformation.ProcessArchitecture.ToString().ToLower();
|
|
||||||
string zip = $"linker-{system}-{arch}.zip";
|
|
||||||
var a = hdc1.DocumentNode.QuerySelectorAll("a").FirstOrDefault(c => c.InnerText.Trim() == zip);
|
|
||||||
|
|
||||||
|
|
||||||
File.WriteAllText(Path.Join(rootPath, "version.txt"), tag);
|
|
||||||
File.WriteAllText(Path.Join(rootPath, "msg.txt"), msg);
|
|
||||||
|
|
||||||
return new UpdateInfo
|
|
||||||
{
|
|
||||||
Msg = msg,
|
|
||||||
Version = tag,
|
|
||||||
Url = $"http://gh.snltty.com:1808/https://github.com{a.GetAttributeValue("href", "").Trim()}"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
catch (Exception)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
sealed class UpdateInfo
|
|
||||||
{
|
|
||||||
public string Version { get; set; }
|
|
||||||
public string Msg { get; set; }
|
|
||||||
public string Url { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public sealed class CommandHelper
|
|
||||||
{
|
|
||||||
|
|
||||||
public static string Execute(string arg, string[] commands, bool readResult = true)
|
|
||||||
{
|
|
||||||
if (OperatingSystem.IsWindows())
|
|
||||||
{
|
|
||||||
return Windows(arg, commands, readResult);
|
|
||||||
}
|
|
||||||
else if (OperatingSystem.IsLinux())
|
|
||||||
{
|
|
||||||
return Linux(arg, commands, readResult);
|
|
||||||
}
|
|
||||||
return Osx(arg, commands, readResult);
|
|
||||||
}
|
|
||||||
public static string Windows(string arg, string[] commands, bool readResult = true)
|
|
||||||
{
|
|
||||||
return Execute("cmd.exe", arg, commands, readResult);
|
|
||||||
}
|
|
||||||
public static string Linux(string arg, string[] commands, bool readResult = true)
|
|
||||||
{
|
|
||||||
return Execute("/bin/bash", arg, commands, readResult);
|
|
||||||
}
|
|
||||||
public static string Osx(string arg, string[] commands, bool readResult = true)
|
|
||||||
{
|
|
||||||
return Execute("/bin/bash", arg, commands, readResult);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Process Execute(string fileName, string arg)
|
|
||||||
{
|
|
||||||
Process proc = new Process();
|
|
||||||
proc.StartInfo.CreateNoWindow = true;
|
|
||||||
proc.StartInfo.FileName = fileName;
|
|
||||||
proc.StartInfo.UseShellExecute = false;
|
|
||||||
proc.StartInfo.RedirectStandardError = true;
|
|
||||||
proc.StartInfo.RedirectStandardInput = true;
|
|
||||||
proc.StartInfo.RedirectStandardOutput = true;
|
|
||||||
proc.StartInfo.Arguments = arg;
|
|
||||||
proc.StartInfo.Verb = "runas";
|
|
||||||
proc.Start();
|
|
||||||
|
|
||||||
//Process proc = Process.Start(fileName, arg);
|
|
||||||
return proc;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string Execute(string fileName, string arg, string[] commands, bool readResult = true)
|
|
||||||
{
|
|
||||||
using Process proc = new Process();
|
|
||||||
proc.StartInfo.WorkingDirectory = Path.GetFullPath(Path.Join("./"));
|
|
||||||
proc.StartInfo.CreateNoWindow = true;
|
|
||||||
proc.StartInfo.FileName = fileName;
|
|
||||||
proc.StartInfo.UseShellExecute = false;
|
|
||||||
proc.StartInfo.RedirectStandardError = true;
|
|
||||||
proc.StartInfo.RedirectStandardInput = true;
|
|
||||||
proc.StartInfo.RedirectStandardOutput = true;
|
|
||||||
proc.StartInfo.Arguments = arg;
|
|
||||||
proc.StartInfo.Verb = "runas";
|
|
||||||
proc.Start();
|
|
||||||
|
|
||||||
if (commands.Length > 0)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < commands.Length; i++)
|
|
||||||
{
|
|
||||||
proc.StandardInput.WriteLine(commands[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
proc.StandardInput.AutoFlush = true;
|
|
||||||
if (readResult)
|
|
||||||
{
|
|
||||||
proc.StandardInput.WriteLine("exit");
|
|
||||||
proc.StandardInput.Close();
|
|
||||||
string output = proc.StandardOutput.ReadToEnd();
|
|
||||||
string error = proc.StandardError.ReadToEnd();
|
|
||||||
proc.WaitForExit();
|
|
||||||
proc.Close();
|
|
||||||
proc.Dispose();
|
|
||||||
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
proc.StandardOutput.Read();
|
|
||||||
proc.Close();
|
|
||||||
proc.Dispose();
|
|
||||||
return string.Empty;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,79 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
|
|
||||||
<assemblyIdentity version="1.0.0.0" name="MyApplication.app"/>
|
|
||||||
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
|
|
||||||
<security>
|
|
||||||
<requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
|
|
||||||
<!-- UAC 清单选项
|
|
||||||
如果想要更改 Windows 用户帐户控制级别,请使用
|
|
||||||
以下节点之一替换 requestedExecutionLevel 节点。
|
|
||||||
|
|
||||||
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
|
|
||||||
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
|
|
||||||
<requestedExecutionLevel level="highestAvailable" uiAccess="false" />
|
|
||||||
|
|
||||||
指定 requestedExecutionLevel 元素将禁用文件和注册表虚拟化。
|
|
||||||
如果你的应用程序需要此虚拟化来实现向后兼容性,则移除此
|
|
||||||
元素。
|
|
||||||
-->
|
|
||||||
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
|
|
||||||
</requestedPrivileges>
|
|
||||||
</security>
|
|
||||||
</trustInfo>
|
|
||||||
|
|
||||||
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
|
|
||||||
<application>
|
|
||||||
<!-- 设计此应用程序与其一起工作且已针对此应用程序进行测试的
|
|
||||||
Windows 版本的列表。取消评论适当的元素,
|
|
||||||
Windows 将自动选择最兼容的环境。 -->
|
|
||||||
|
|
||||||
<!-- Windows Vista -->
|
|
||||||
<!--<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}" />-->
|
|
||||||
|
|
||||||
<!-- Windows 7 -->
|
|
||||||
<!--<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" />-->
|
|
||||||
|
|
||||||
<!-- Windows 8 -->
|
|
||||||
<!--<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" />-->
|
|
||||||
|
|
||||||
<!-- Windows 8.1 -->
|
|
||||||
<!--<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}" />-->
|
|
||||||
|
|
||||||
<!-- Windows 10 -->
|
|
||||||
<!--<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />-->
|
|
||||||
|
|
||||||
</application>
|
|
||||||
</compatibility>
|
|
||||||
|
|
||||||
<!-- 指示该应用程序可感知 DPI 且 Windows 在 DPI 较高时将不会对其进行
|
|
||||||
自动缩放。Windows Presentation Foundation (WPF)应用程序自动感知 DPI,无需
|
|
||||||
选择加入。选择加入此设置的 Windows 窗体应用程序(面向 .NET Framework 4.6)还应
|
|
||||||
在其 app.config 中将 "EnableWindowsFormsHighDpiAutoResizing" 设置设置为 "true"。
|
|
||||||
|
|
||||||
将应用程序设为感知长路径。请参阅 https://docs.microsoft.com/windows/win32/fileio/maximum-file-path-limitation -->
|
|
||||||
<!--
|
|
||||||
<application xmlns="urn:schemas-microsoft-com:asm.v3">
|
|
||||||
<windowsSettings>
|
|
||||||
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware>
|
|
||||||
<longPathAware xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">true</longPathAware>
|
|
||||||
</windowsSettings>
|
|
||||||
</application>
|
|
||||||
-->
|
|
||||||
|
|
||||||
<!-- 启用 Windows 公共控件和对话框的主题(Windows XP 和更高版本) -->
|
|
||||||
<!--
|
|
||||||
<dependency>
|
|
||||||
<dependentAssembly>
|
|
||||||
<assemblyIdentity
|
|
||||||
type="win32"
|
|
||||||
name="Microsoft.Windows.Common-Controls"
|
|
||||||
version="6.0.0.0"
|
|
||||||
processorArchitecture="*"
|
|
||||||
publicKeyToken="6595b64144ccf1df"
|
|
||||||
language="*"
|
|
||||||
/>
|
|
||||||
</dependentAssembly>
|
|
||||||
</dependency>
|
|
||||||
-->
|
|
||||||
|
|
||||||
</assembly>
|
|
Binary file not shown.
Before Width: | Height: | Size: 28 KiB |
@@ -1,50 +0,0 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<OutputType>Exe</OutputType>
|
|
||||||
<TargetFrameworks>net8.0</TargetFrameworks>
|
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
|
||||||
<Nullable>disable</Nullable>
|
|
||||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
|
||||||
<BuiltInComInteropSupport>true</BuiltInComInteropSupport>
|
|
||||||
<Configurations>Debug;Release</Configurations>
|
|
||||||
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
|
|
||||||
<PublishAot>false</PublishAot>
|
|
||||||
<JsonSerializerIsReflectionEnabledByDefault>true</JsonSerializerIsReflectionEnabledByDefault>
|
|
||||||
<EnablePreviewFeatures>true</EnablePreviewFeatures>
|
|
||||||
<Version>1.1.1</Version>
|
|
||||||
<Authors>snltty</Authors>
|
|
||||||
<Company>snltty</Company>
|
|
||||||
<Description>snltty</Description>
|
|
||||||
<Copyright>snltty</Copyright>
|
|
||||||
<PackageProjectUrl>https://github.com/snltty/linker</PackageProjectUrl>
|
|
||||||
<RepositoryUrl>https://github.com/snltty/linker</RepositoryUrl>
|
|
||||||
<PackageReleaseNotes>snltty updater</PackageReleaseNotes>
|
|
||||||
<AssemblyVersion>1.1.1.3</AssemblyVersion>
|
|
||||||
<FileVersion>1.1.1.3</FileVersion>
|
|
||||||
<ApplicationIcon>favicon.ico</ApplicationIcon>
|
|
||||||
<ApplicationManifest>app.manifest</ApplicationManifest>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
|
||||||
<DebugType>full</DebugType>
|
|
||||||
<DebugSymbols>true</DebugSymbols>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
|
||||||
<DebugType>none</DebugType>
|
|
||||||
<DebugSymbols>false</DebugSymbols>
|
|
||||||
<Optimize>True</Optimize>
|
|
||||||
</PropertyGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<Content Include="favicon.ico" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<PackageReference Include="Fizzler.Systems.HtmlAgilityPack" Version="1.2.1" />
|
|
||||||
<PackageReference Include="System.IO.Compression.ZipFile" Version="4.3.0" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<ProjectReference Include="..\linker.libs\linker.libs.csproj" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
</Project>
|
|
@@ -3,4 +3,10 @@ import { sendWebsocketMsg } from './request'
|
|||||||
|
|
||||||
export const getUpdater = () => {
|
export const getUpdater = () => {
|
||||||
return sendWebsocketMsg('updaterclient/get');
|
return sendWebsocketMsg('updaterclient/get');
|
||||||
|
}
|
||||||
|
export const confirm = (machineId) => {
|
||||||
|
return sendWebsocketMsg('updaterclient/confirm', machineId);
|
||||||
|
}
|
||||||
|
export const exit = (machineId) => {
|
||||||
|
return sendWebsocketMsg('updaterclient/exit', machineId);
|
||||||
}
|
}
|
@@ -50,9 +50,9 @@ export default {
|
|||||||
steps:['选择模式','服务端','客户端','完成']
|
steps:['选择模式','服务端','客户端','完成']
|
||||||
});
|
});
|
||||||
|
|
||||||
watch(() => globalData.value.configed, (val) => {
|
watch(() => globalData.value.config.configed, (val) => {
|
||||||
if (val) {
|
if (val) {
|
||||||
state.show = globalData.value.connected && globalData.value.configed && globalData.value.config.Common.Install == false;
|
state.show = globalData.value.api.connected && globalData.value.config.configed && globalData.value.config.Common.Install == false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="status-api-wrap" :class="{connected:connected}">
|
<div class="status-api-wrap" :class="{connected:connected}">
|
||||||
<el-popconfirm confirm-button-text="清除" cancel-button-text="更改" title="确定你的惭怍?" @cancel="handleShow" @confirm="handleResetConnect" >
|
<el-popconfirm confirm-button-text="清除" cancel-button-text="更改" title="确定你的操作?" @cancel="handleShow" @confirm="handleResetConnect" >
|
||||||
<template #reference>
|
<template #reference>
|
||||||
<a href="javascript:;" >
|
<a href="javascript:;" >
|
||||||
<el-icon size="16"><Tools /></el-icon>
|
<el-icon size="16"><Tools /></el-icon>
|
||||||
@@ -32,29 +32,28 @@ import { initWebsocket, subWebsocketState,closeWebsocket } from '../../apis/requ
|
|||||||
import { getSignInfo } from '../../apis/signin'
|
import { getSignInfo } from '../../apis/signin'
|
||||||
import { getConfig } from '../../apis/config'
|
import { getConfig } from '../../apis/config'
|
||||||
import {Tools} from '@element-plus/icons-vue'
|
import {Tools} from '@element-plus/icons-vue'
|
||||||
import { getUpdater } from '@/apis/updater';
|
|
||||||
export default {
|
export default {
|
||||||
components:{Tools},
|
components:{Tools},
|
||||||
setup(props) {
|
setup(props) {
|
||||||
const globalData = injectGlobalData();
|
const globalData = injectGlobalData();
|
||||||
const connected = computed(()=>globalData.value.connected);
|
const connected = computed(()=>globalData.value.api.connected);
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
|
|
||||||
const defaultInfo = {api:`${window.location.hostname}:1803`,psd:'snltty'};
|
const defaultInfo = {api:`${window.location.hostname}:1803`,psd:'snltty'};
|
||||||
const handleResetConnect = () => {
|
|
||||||
localStorage.setItem('api-cache', '');
|
|
||||||
router.push({name:route.name});
|
|
||||||
window.location.reload();
|
|
||||||
}
|
|
||||||
const queryCache = JSON.parse(localStorage.getItem('api-cache') || JSON.stringify(defaultInfo));
|
const queryCache = JSON.parse(localStorage.getItem('api-cache') || JSON.stringify(defaultInfo));
|
||||||
const state = reactive({
|
const state = reactive({
|
||||||
api:queryCache.api,
|
api:queryCache.api,
|
||||||
psd:queryCache.psd,
|
psd:queryCache.psd,
|
||||||
showPort: false
|
showPort: false
|
||||||
});
|
});
|
||||||
const showPort = computed(() => globalData.value.connected == false && state.showPort);
|
const showPort = computed(() => globalData.value.api.connected == false && state.showPort);
|
||||||
|
|
||||||
|
const handleResetConnect = () => {
|
||||||
|
localStorage.setItem('api-cache', '');
|
||||||
|
router.push({name:route.name});
|
||||||
|
window.location.reload();
|
||||||
|
}
|
||||||
const handleConnect = () => {
|
const handleConnect = () => {
|
||||||
queryCache.api = state.api;
|
queryCache.api = state.api;
|
||||||
queryCache.psd = state.psd;
|
queryCache.psd = state.psd;
|
||||||
@@ -78,7 +77,7 @@ export default {
|
|||||||
globalData.value.config.Client = res.Client;
|
globalData.value.config.Client = res.Client;
|
||||||
globalData.value.config.Server = res.Server;
|
globalData.value.config.Server = res.Server;
|
||||||
globalData.value.config.Running = res.Running;
|
globalData.value.config.Running = res.Running;
|
||||||
globalData.value.configed = true;
|
globalData.value.config.configed = true;
|
||||||
setTimeout(()=>{
|
setTimeout(()=>{
|
||||||
_getConfig();
|
_getConfig();
|
||||||
},1000);
|
},1000);
|
||||||
@@ -102,21 +101,12 @@ export default {
|
|||||||
},1000);
|
},1000);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
const _getUpdater = ()=>{
|
|
||||||
getUpdater().then((res)=>{
|
|
||||||
if(res){
|
|
||||||
globalData.value.updater.Version = res.Version;
|
|
||||||
globalData.value.updater.Msg = res.Msg;
|
|
||||||
}
|
|
||||||
}).catch((err)=>{});
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
setTimeout(() => { state.showPort = true; }, 100);
|
setTimeout(() => { state.showPort = true; }, 500);
|
||||||
subWebsocketState((state) => { if (state) {
|
subWebsocketState((state) => { if (state) {
|
||||||
_getConfig();
|
_getConfig();
|
||||||
_getSignInfoInfo();
|
_getSignInfoInfo();
|
||||||
_getUpdater();
|
|
||||||
}});
|
}});
|
||||||
router.isReady().then(()=>{
|
router.isReady().then(()=>{
|
||||||
state.api = route.query.api ?`${window.location.hostname}:${route.query.api}` : state.api;
|
state.api = route.query.api ?`${window.location.hostname}:${route.query.api}` : state.api;
|
||||||
|
@@ -63,7 +63,6 @@ export default {
|
|||||||
state.loading = false;
|
state.loading = false;
|
||||||
state.show = false;
|
state.show = false;
|
||||||
ElMessage.success('已操作');
|
ElMessage.success('已操作');
|
||||||
globalData.value.updateFlag = Date.now();
|
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
state.loading = false;
|
state.loading = false;
|
||||||
ElMessage.success('操作失败!');
|
ElMessage.success('操作失败!');
|
||||||
|
@@ -1,22 +1,19 @@
|
|||||||
import { subWebsocketState } from "@/apis/request";
|
import { subWebsocketState } from "@/apis/request";
|
||||||
import { computed, inject, provide, ref } from "vue";
|
import { inject, provide, ref } from "vue";
|
||||||
|
|
||||||
const globalDataSymbol = Symbol();
|
const globalDataSymbol = Symbol();
|
||||||
|
|
||||||
export const provideGlobalData = () => {
|
export const provideGlobalData = () => {
|
||||||
const globalData = ref({
|
const globalData = ref({
|
||||||
//已连接
|
//已连接
|
||||||
connected: false,
|
api: { connected: false },
|
||||||
updateFlag: false,
|
|
||||||
height: 0,
|
height: 0,
|
||||||
config: { Common: {}, Client: {}, Server: {}, Running: {} },
|
config: { Common: {}, Client: {}, Server: {}, Running: {}, configed: false },
|
||||||
configed: false,
|
|
||||||
signin: { Connected: false, Connecting: false, Version: 'v1.0.0.0' },
|
signin: { Connected: false, Connecting: false, Version: 'v1.0.0.0' },
|
||||||
bufferSize: ['1KB', '2KB', '4KB', '8KB', '16KB', '32KB', '64KB', '128KB', '256KB', '512KB', '1024KB'],
|
bufferSize: ['1KB', '2KB', '4KB', '8KB', '16KB', '32KB', '64KB', '128KB', '256KB', '512KB', '1024KB']
|
||||||
updater: { Msg: '', Version: '' }
|
|
||||||
});
|
});
|
||||||
subWebsocketState((state) => {
|
subWebsocketState((state) => {
|
||||||
globalData.value.connected = state;
|
globalData.value.api.connected = state;
|
||||||
});
|
});
|
||||||
|
|
||||||
provide(globalDataSymbol, globalData);
|
provide(globalDataSymbol, globalData);
|
||||||
|
@@ -18,16 +18,29 @@
|
|||||||
<p class="flex">
|
<p class="flex">
|
||||||
<span>{{ scope.row.IP }}</span>
|
<span>{{ scope.row.IP }}</span>
|
||||||
<span class="flex-1"></span>
|
<span class="flex-1"></span>
|
||||||
<a href="javascript:;" class="download" title="下载更新" @click="handleUpdate">
|
<a href="javascript:;" class="download" title="下载更新" @click="handleUpdate(scope.row)">
|
||||||
<template v-if="scope.row.Version != version && scope.row.Version == updater.Version">
|
<span :title="updateText(scope.row)" :class="updateColor(scope.row)">
|
||||||
<span title="与服务器版本不一致,建议更新">{{scope.row.Version}}<el-icon size="14"><Download /></el-icon></span>
|
<span>{{scope.row.Version}}</span>
|
||||||
</template>
|
<template v-if="updater.list[scope.row.MachineId]">
|
||||||
<template v-else-if="scope.row.Version != updater.Version">
|
<template v-if="updater.list[scope.row.MachineId].Status == 1">
|
||||||
<span title="不是最新版本,建议更新">{{scope.row.Version}}<el-icon size="14"><Download /></el-icon></span>
|
<el-icon size="14" class="loading"><Loading /></el-icon>
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else-if="updater.list[scope.row.MachineId].Status == 2">
|
||||||
<span title="版本一致,但我无法阻止你喜欢更新" class="green">{{scope.row.Version}}</span>
|
<el-icon size="14"><Download /></el-icon>
|
||||||
</template>
|
</template>
|
||||||
|
<template v-else-if="updater.list[scope.row.MachineId].Status == 3 || updater.list[scope.row.MachineId].Status == 5">
|
||||||
|
<el-icon size="14" class="loading"><Loading /></el-icon>
|
||||||
|
<span class="progress" v-if="updater.list[scope.row.MachineId].Length ==0">0%</span>
|
||||||
|
<span class="progress" v-else>{{parseInt(updater.list[scope.row.MachineId].Current/updater.list[scope.row.MachineId].Length*100)}}%</span>
|
||||||
|
</template>
|
||||||
|
<template v-else-if="updater.list[scope.row.MachineId].Status == 6">
|
||||||
|
<el-icon size="14" class="green"><CircleCheck /></el-icon>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<el-icon size="14"><Download /></el-icon>
|
||||||
|
</template>
|
||||||
|
</span>
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -37,51 +50,124 @@
|
|||||||
<script>
|
<script>
|
||||||
import { injectGlobalData } from '@/provide';
|
import { injectGlobalData } from '@/provide';
|
||||||
import { computed, ref } from 'vue';
|
import { computed, ref } from 'vue';
|
||||||
import {WarnTriangleFilled,StarFilled,Search,Download} from '@element-plus/icons-vue'
|
import {StarFilled,Search,Download,Loading,CircleCheck} from '@element-plus/icons-vue'
|
||||||
import { ElMessageBox } from 'element-plus';
|
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||||
|
import { confirm, exit } from '@/apis/updater';
|
||||||
|
import { useUpdater } from './updater';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
emits:['edit','refresh'],
|
emits:['edit','refresh'],
|
||||||
components:{WarnTriangleFilled,StarFilled,Search,Download},
|
components:{StarFilled,Search,Download,Loading,CircleCheck},
|
||||||
setup(props,{emit}) {
|
setup(props,{emit}) {
|
||||||
|
|
||||||
const globalData = injectGlobalData();
|
const globalData = injectGlobalData();
|
||||||
const version = computed(()=>globalData.value.signin.Version);
|
const version = computed(()=>globalData.value.signin.Version);
|
||||||
const updater = computed(()=>globalData.value.updater);
|
const name = ref(sessionStorage.getItem('search-name') || '');
|
||||||
const name = ref('')
|
const updater = useUpdater();
|
||||||
|
|
||||||
|
const updateText = (row)=>{
|
||||||
|
if(!updater.value.list[row.MachineId]){
|
||||||
|
return '未检测到更新';
|
||||||
|
}
|
||||||
|
if(updater.value.list[row.MachineId].Status <= 2) {
|
||||||
|
return row.Version != version.value
|
||||||
|
? '与服务器版本不一致,建议更新'
|
||||||
|
: updater.value.list[row.MachineId].Version != row.Version
|
||||||
|
? '不是最新版本,建议更新' : '版本一致,但我无法阻止你喜欢更新'
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
3:'正在下载',
|
||||||
|
4:'已下载',
|
||||||
|
5:'正在解压',
|
||||||
|
6:'已解压,请重启',
|
||||||
|
}[updater.value.list[row.MachineId].Status];
|
||||||
|
}
|
||||||
|
const updateColor = (row)=>{
|
||||||
|
return row.Version != version.value
|
||||||
|
? 'red'
|
||||||
|
: updater.value.list[row.MachineId] && updater.value.list[row.MachineId].Version != row.Version
|
||||||
|
? 'yellow' :'green'
|
||||||
|
}
|
||||||
|
|
||||||
const handleEdit = (row)=>{
|
const handleEdit = (row)=>{
|
||||||
emit('edit',row)
|
emit('edit',row)
|
||||||
}
|
}
|
||||||
const handleRefresh = ()=>{
|
const handleRefresh = ()=>{
|
||||||
|
sessionStorage.setItem('search-name',name.value);
|
||||||
emit('refresh',name.value)
|
emit('refresh',name.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleUpdate = ()=>{
|
const handleUpdate = (row)=>{
|
||||||
ElMessageBox.confirm('将进入后台自动更新,更新完成后自动重启', '是否下载更新?', {
|
const updateInfo =updater.value.list[row.MachineId];
|
||||||
confirmButtonText: '确定',
|
if(!updateInfo){
|
||||||
cancelButtonText: '取消',
|
ElMessage.error('未检测到更新');
|
||||||
type: 'warning'
|
return;
|
||||||
}).then(() => {
|
}
|
||||||
}).catch(() => {});
|
//未检测,检测中,下载中,解压中
|
||||||
|
if([0,1,3,5].indexOf(updateInfo.Status)>=0){
|
||||||
|
ElMessage.error('操作中,请稍后!');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
//已检测
|
||||||
|
if(updateInfo.Status == 2){
|
||||||
|
ElMessageBox.confirm('确定要更新吗?', '提示', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning'
|
||||||
|
}).then(() => {
|
||||||
|
confirm(row.MachineId);
|
||||||
|
}).catch(() => {});
|
||||||
|
}
|
||||||
|
//已解压
|
||||||
|
else if(updateInfo.Status == 6){
|
||||||
|
|
||||||
|
ElMessageBox.confirm('确定关闭程序吗?如果你是以服务形式安装,关闭后应该会自动再次启动', '提示', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning'
|
||||||
|
}).then(() => {
|
||||||
|
exit(row.MachineId);
|
||||||
|
}).catch(() => {});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
updater, handleEdit,handleRefresh,version,name,handleUpdate
|
handleEdit,handleRefresh,version,name,updater,updateText,updateColor,handleUpdate
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<style lang="stylus" scoped>
|
<style lang="stylus" scoped>
|
||||||
|
|
||||||
|
@keyframes loading {
|
||||||
|
from{transform:rotate(0deg)}
|
||||||
|
to{transform:rotate(360deg)}
|
||||||
|
}
|
||||||
|
|
||||||
a{
|
a{
|
||||||
color:#666;
|
color:#666;
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
|
&.green{color:green;font-weight:bold;}
|
||||||
}
|
}
|
||||||
a.green{color:green}
|
|
||||||
|
|
||||||
a.download{
|
a.download{
|
||||||
margin-left:.6rem
|
margin-left:.6rem
|
||||||
.el-icon{vertical-align:middle;color:red;font-weight:bold;}
|
.el-icon{
|
||||||
|
vertical-align:middle;font-weight:bold;
|
||||||
|
&.green{color:green}
|
||||||
|
&.red{color:red}
|
||||||
|
|
||||||
|
&.loading{
|
||||||
|
animation:loading 1s linear infinite;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
span{
|
||||||
|
&.green{color:green}
|
||||||
|
&.red{color:red}
|
||||||
|
&.yellow{color:#e68906}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-input{
|
.el-input{
|
||||||
|
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
<el-table-column prop="forward" label="端口转发">
|
<el-table-column prop="forward" label="端口转发">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<template v-if="scope.row.showForward">
|
<template v-if="!scope.row.isSelf">
|
||||||
<div>
|
<div>
|
||||||
<ul class="list forward">
|
<ul class="list forward">
|
||||||
<template v-if="forward.list[scope.row.MachineId] && forward.list[scope.row.MachineId].length > 0">
|
<template v-if="forward.list[scope.row.MachineId] && forward.list[scope.row.MachineId].length > 0">
|
||||||
@@ -25,7 +25,7 @@
|
|||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template v-else-if="scope.row.showSForward">
|
<template v-else-if="!scope.row.isSelf">
|
||||||
<div>
|
<div>
|
||||||
<ul class="list sforward">
|
<ul class="list sforward">
|
||||||
<template v-if="sforward.list && sforward.list.length > 0">
|
<template v-if="sforward.list && sforward.list.length > 0">
|
||||||
|
@@ -55,6 +55,7 @@ import { provideForward } from './forward'
|
|||||||
import { provideConnections } from './connections'
|
import { provideConnections } from './connections'
|
||||||
import { provideSforward } from './sforward'
|
import { provideSforward } from './sforward'
|
||||||
import { provideDevices } from './devices'
|
import { provideDevices } from './devices'
|
||||||
|
import { provideUpdater } from './updater'
|
||||||
export default {
|
export default {
|
||||||
components: {Device,DeviceEdit,Tunnel,TunnelEdit,ConnectionsEdit, Tuntap,TuntapEdit, Forward,ForwardEdit,ForwardCopy,SForwardEdit,SForwardCopy },
|
components: {Device,DeviceEdit,Tunnel,TunnelEdit,ConnectionsEdit, Tuntap,TuntapEdit, Forward,ForwardEdit,ForwardCopy,SForwardEdit,SForwardCopy },
|
||||||
setup(props) {
|
setup(props) {
|
||||||
@@ -77,6 +78,7 @@ export default {
|
|||||||
handleTunnelConnections,clearConnectionsTimeout
|
handleTunnelConnections,clearConnectionsTimeout
|
||||||
} = provideConnections();
|
} = provideConnections();
|
||||||
|
|
||||||
|
const {_getUpdater,clearUpdaterTimeout} = provideUpdater();
|
||||||
|
|
||||||
const _handleForwardEdit = (machineId) => {
|
const _handleForwardEdit = (machineId) => {
|
||||||
handleForwardEdit(machineId,devices.page.List.filter(c => c.MachineId == machineId)[0].MachineName);
|
handleForwardEdit(machineId,devices.page.List.filter(c => c.MachineId == machineId)[0].MachineName);
|
||||||
@@ -85,8 +87,11 @@ export default {
|
|||||||
const handlePageRefresh = (name)=>{
|
const handlePageRefresh = (name)=>{
|
||||||
devices.page.Request.Name = name || '';
|
devices.page.Request.Name = name || '';
|
||||||
if(devices.page.Request.Name){
|
if(devices.page.Request.Name){
|
||||||
|
//从虚拟网卡里查找
|
||||||
devices.page.Request.Ids = getTuntapMachines(devices.page.Request.Name)
|
devices.page.Request.Ids = getTuntapMachines(devices.page.Request.Name)
|
||||||
|
//从端口转发里查找
|
||||||
.concat(getForwardMachines(devices.page.Request.Name))
|
.concat(getForwardMachines(devices.page.Request.Name))
|
||||||
|
//从服务器代理穿透里查找
|
||||||
.concat(getSForwardMachines(devices.page.Request.Name))
|
.concat(getSForwardMachines(devices.page.Request.Name))
|
||||||
.reduce((arr,id)=>{
|
.reduce((arr,id)=>{
|
||||||
if(arr.indexOf(id) == -1){
|
if(arr.indexOf(id) == -1){
|
||||||
@@ -125,6 +130,8 @@ export default {
|
|||||||
_getForwardInfo();
|
_getForwardInfo();
|
||||||
_getSForwardInfo();
|
_getSForwardInfo();
|
||||||
|
|
||||||
|
_getUpdater();
|
||||||
|
|
||||||
_testTargetForwardInfo();
|
_testTargetForwardInfo();
|
||||||
_testListenForwardInfo();
|
_testListenForwardInfo();
|
||||||
_testLocalSForwardInfo();
|
_testLocalSForwardInfo();
|
||||||
@@ -136,6 +143,8 @@ export default {
|
|||||||
clearTunnelTimeout();
|
clearTunnelTimeout();
|
||||||
clearForwardTimeout();
|
clearForwardTimeout();
|
||||||
clearSForwardTimeout();
|
clearSForwardTimeout();
|
||||||
|
|
||||||
|
clearUpdaterTimeout();
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@@ -21,7 +21,7 @@ export const provideConnections = () => {
|
|||||||
});
|
});
|
||||||
provide(forwardConnectionsSymbol, forwardConnections);
|
provide(forwardConnectionsSymbol, forwardConnections);
|
||||||
const _getForwardConnections = () => {
|
const _getForwardConnections = () => {
|
||||||
if (globalData.value.connected) {
|
if (globalData.value.api.connected) {
|
||||||
getForwardConnections().then((res) => {
|
getForwardConnections().then((res) => {
|
||||||
parseConnections(res, removeForwardConnection);
|
parseConnections(res, removeForwardConnection);
|
||||||
forwardConnections.value.list = res;
|
forwardConnections.value.list = res;
|
||||||
@@ -39,7 +39,7 @@ export const provideConnections = () => {
|
|||||||
});
|
});
|
||||||
provide(tuntapConnectionsSymbol, tuntapConnections);
|
provide(tuntapConnectionsSymbol, tuntapConnections);
|
||||||
const _getTuntapConnections = () => {
|
const _getTuntapConnections = () => {
|
||||||
if (globalData.value.connected) {
|
if (globalData.value.api.connected) {
|
||||||
getTuntapConnections().then((res) => {
|
getTuntapConnections().then((res) => {
|
||||||
parseConnections(res, removeTuntapConnection);
|
parseConnections(res, removeTuntapConnection);
|
||||||
tuntapConnections.value.list = res;
|
tuntapConnections.value.list = res;
|
||||||
|
@@ -24,9 +24,6 @@ export const provideDevices = () => {
|
|||||||
devices.page.Request = res.Request;
|
devices.page.Request = res.Request;
|
||||||
devices.page.Count = res.Count;
|
devices.page.Count = res.Count;
|
||||||
for (let j in res.List) {
|
for (let j in res.List) {
|
||||||
res.List[j].showTunnel = machineId.value != res.List[j].MachineId;
|
|
||||||
res.List[j].showForward = machineId.value != res.List[j].MachineId;
|
|
||||||
res.List[j].showSForward = machineId.value == res.List[j].MachineId;
|
|
||||||
res.List[j].showDel = machineId.value != res.List[j].MachineId && res.List[j].Connected == false;
|
res.List[j].showDel = machineId.value != res.List[j].MachineId && res.List[j].Connected == false;
|
||||||
res.List[j].isSelf = machineId.value == res.List[j].MachineId;
|
res.List[j].isSelf = machineId.value == res.List[j].MachineId;
|
||||||
}
|
}
|
||||||
@@ -34,7 +31,7 @@ export const provideDevices = () => {
|
|||||||
}).catch((err) => { });
|
}).catch((err) => { });
|
||||||
}
|
}
|
||||||
const _getSignList1 = () => {
|
const _getSignList1 = () => {
|
||||||
if (globalData.value.connected) {
|
if (globalData.value.api.connected) {
|
||||||
getSignInList(devices.page.Request).then((res) => {
|
getSignInList(devices.page.Request).then((res) => {
|
||||||
for (let j in res.List) {
|
for (let j in res.List) {
|
||||||
const item = devices.page.List.filter(c => c.MachineId == res.List[j].MachineId)[0];
|
const item = devices.page.List.filter(c => c.MachineId == res.List[j].MachineId)[0];
|
||||||
@@ -43,9 +40,6 @@ export const provideDevices = () => {
|
|||||||
item.Version = res.List[j].Version;
|
item.Version = res.List[j].Version;
|
||||||
item.LastSignIn = res.List[j].LastSignIn;
|
item.LastSignIn = res.List[j].LastSignIn;
|
||||||
item.Args = res.List[j].Args;
|
item.Args = res.List[j].Args;
|
||||||
item.showTunnel = machineId.value != res.List[j].MachineId;
|
|
||||||
item.showForward = machineId.value != res.List[j].MachineId;
|
|
||||||
item.showSForward = machineId.value == res.List[j].MachineId;
|
|
||||||
item.showDel = machineId.value != res.List[j].MachineId && res.List[j].Connected == false;
|
item.showDel = machineId.value != res.List[j].MachineId && res.List[j].Connected == false;
|
||||||
item.isSelf = machineId.value == res.List[j].MachineId;
|
item.isSelf = machineId.value == res.List[j].MachineId;
|
||||||
}
|
}
|
||||||
|
@@ -16,15 +16,15 @@ export const provideForward = () => {
|
|||||||
});
|
});
|
||||||
provide(forwardSymbol, forward);
|
provide(forwardSymbol, forward);
|
||||||
const _getForwardInfo = () => {
|
const _getForwardInfo = () => {
|
||||||
if (globalData.value.connected) {
|
if (globalData.value.api.connected) {
|
||||||
getForwardInfo().then((res) => {
|
getForwardInfo().then((res) => {
|
||||||
forward.value.list = res;
|
forward.value.list = res;
|
||||||
forward.value.timer = setTimeout(_getForwardInfo, 1000);
|
forward.value.timer = setTimeout(_getForwardInfo, 1020);
|
||||||
}).catch(() => {
|
}).catch(() => {
|
||||||
forward.value.timer = setTimeout(_getForwardInfo, 1000);
|
forward.value.timer = setTimeout(_getForwardInfo, 1020);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
forward.value.timer = setTimeout(_getForwardInfo, 1000);
|
forward.value.timer = setTimeout(_getForwardInfo, 1020);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const handleForwardEdit = (machineId, machineName) => {
|
const handleForwardEdit = (machineId, machineName) => {
|
||||||
|
@@ -16,15 +16,15 @@ export const provideSforward = () => {
|
|||||||
});
|
});
|
||||||
provide(sforwardSymbol, sforward);
|
provide(sforwardSymbol, sforward);
|
||||||
const _getSForwardInfo = () => {
|
const _getSForwardInfo = () => {
|
||||||
if (globalData.value.connected) {
|
if (globalData.value.api.connected) {
|
||||||
getSForwardInfo().then((res) => {
|
getSForwardInfo().then((res) => {
|
||||||
sforward.value.list = res;
|
sforward.value.list = res;
|
||||||
sforward.value.timer = setTimeout(_getSForwardInfo, 1000);
|
sforward.value.timer = setTimeout(_getSForwardInfo, 1040);
|
||||||
}).catch(() => {
|
}).catch(() => {
|
||||||
sforward.value.timer = setTimeout(_getSForwardInfo, 1000);
|
sforward.value.timer = setTimeout(_getSForwardInfo, 1040);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
sforward.value.timer = setTimeout(_getSForwardInfo, 1000);
|
sforward.value.timer = setTimeout(_getSForwardInfo, 1040);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const handleSForwardEdit = () => {
|
const handleSForwardEdit = () => {
|
||||||
|
@@ -15,18 +15,18 @@ export const provideTunnel = () => {
|
|||||||
});
|
});
|
||||||
provide(tunnelSymbol, tunnel);
|
provide(tunnelSymbol, tunnel);
|
||||||
const _getTunnelInfo = () => {
|
const _getTunnelInfo = () => {
|
||||||
if (globalData.value.connected) {
|
if (globalData.value.api.connected) {
|
||||||
getTunnelInfo(tunnel.value.hashcode.toString()).then((res) => {
|
getTunnelInfo(tunnel.value.hashcode.toString()).then((res) => {
|
||||||
tunnel.value.hashcode = res.HashCode;
|
tunnel.value.hashcode = res.HashCode;
|
||||||
if (res.List) {
|
if (res.List) {
|
||||||
tunnel.value.list = res.List;
|
tunnel.value.list = res.List;
|
||||||
}
|
}
|
||||||
tunnel.value.timer = setTimeout(_getTunnelInfo, 1000);
|
tunnel.value.timer = setTimeout(_getTunnelInfo, 1060);
|
||||||
}).catch(() => {
|
}).catch(() => {
|
||||||
tunnel.value.timer = setTimeout(_getTunnelInfo, 1000);
|
tunnel.value.timer = setTimeout(_getTunnelInfo, 1060);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
tunnel.value.timer = setTimeout(_getTunnelInfo, 1000);
|
tunnel.value.timer = setTimeout(_getTunnelInfo, 1060);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const handleTunnelEdit = (_tunnel) => {
|
const handleTunnelEdit = (_tunnel) => {
|
||||||
|
@@ -16,7 +16,7 @@ export const provideTuntap = () => {
|
|||||||
provide(tuntapSymbol, tuntap);
|
provide(tuntapSymbol, tuntap);
|
||||||
const _getTuntapInfo = () => {
|
const _getTuntapInfo = () => {
|
||||||
clearTimeout(tuntap.value.timer);
|
clearTimeout(tuntap.value.timer);
|
||||||
if (globalData.value.connected) {
|
if (globalData.value.api.connected) {
|
||||||
getTuntapInfo(tuntap.value.hashcode.toString()).then((res) => {
|
getTuntapInfo(tuntap.value.hashcode.toString()).then((res) => {
|
||||||
tuntap.value.hashcode = res.HashCode;
|
tuntap.value.hashcode = res.HashCode;
|
||||||
if (res.List) {
|
if (res.List) {
|
||||||
|
37
linker.web/src/views/devices/updater.js
Normal file
37
linker.web/src/views/devices/updater.js
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
import { getUpdater } from "@/apis/updater";
|
||||||
|
import { injectGlobalData } from "@/provide";
|
||||||
|
import { inject, provide, ref } from "vue";
|
||||||
|
|
||||||
|
const updaterSymbol = Symbol();
|
||||||
|
export const provideUpdater = () => {
|
||||||
|
const globalData = injectGlobalData();
|
||||||
|
const updater = ref({
|
||||||
|
timer: 0,
|
||||||
|
list: {}
|
||||||
|
});
|
||||||
|
provide(updaterSymbol, updater);
|
||||||
|
const _getUpdater = () => {
|
||||||
|
if (globalData.value.api.connected) {
|
||||||
|
getUpdater().then((res) => {
|
||||||
|
updater.value.list = res;
|
||||||
|
updater.value.timer = setTimeout(_getUpdater, 800);
|
||||||
|
}).catch(() => {
|
||||||
|
updater.value.timer = setTimeout(_getUpdater, 800);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
updater.value.timer = setTimeout(_getUpdater, 800);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const clearUpdaterTimeout = () => {
|
||||||
|
clearTimeout(updater.value.timer);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return {
|
||||||
|
updater, _getUpdater, clearUpdaterTimeout
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export const useUpdater = () => {
|
||||||
|
return inject(updaterSymbol);
|
||||||
|
}
|
@@ -21,7 +21,7 @@ export default {
|
|||||||
const globalData = injectGlobalData();
|
const globalData = injectGlobalData();
|
||||||
const state = reactive({
|
const state = reactive({
|
||||||
tab:settingComponents[0].name,
|
tab:settingComponents[0].name,
|
||||||
connected:computed(()=>globalData.value.connected && globalData.value.configed),
|
connected:computed(()=>globalData.value.api.connected && globalData.value.config.configed),
|
||||||
});
|
});
|
||||||
return {
|
return {
|
||||||
state,settingComponents
|
state,settingComponents
|
||||||
|
@@ -17,7 +17,7 @@
|
|||||||
<EnablePreviewFeatures>true</EnablePreviewFeatures>
|
<EnablePreviewFeatures>true</EnablePreviewFeatures>
|
||||||
<ServerGarbageCollection>false</ServerGarbageCollection>
|
<ServerGarbageCollection>false</ServerGarbageCollection>
|
||||||
<Title>linker</Title>
|
<Title>linker</Title>
|
||||||
<Version>1.1.1</Version>
|
<Version>1.1.2</Version>
|
||||||
<Authors>snltty</Authors>
|
<Authors>snltty</Authors>
|
||||||
<Company>snltty</Company>
|
<Company>snltty</Company>
|
||||||
<Description>linker</Description>
|
<Description>linker</Description>
|
||||||
@@ -25,8 +25,8 @@
|
|||||||
<PackageProjectUrl>https://github.com/snltty/linker</PackageProjectUrl>
|
<PackageProjectUrl>https://github.com/snltty/linker</PackageProjectUrl>
|
||||||
<RepositoryUrl>https://github.com/snltty/linker</RepositoryUrl>
|
<RepositoryUrl>https://github.com/snltty/linker</RepositoryUrl>
|
||||||
<PackageReleaseNotes>linker</PackageReleaseNotes>
|
<PackageReleaseNotes>linker</PackageReleaseNotes>
|
||||||
<AssemblyVersion>1.1.1.3</AssemblyVersion>
|
<AssemblyVersion>1.1.2.1</AssemblyVersion>
|
||||||
<FileVersion>1.1.1.3</FileVersion>
|
<FileVersion>1.1.2.1</FileVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
|
||||||
|
@@ -3,6 +3,7 @@ using linker.libs.extends;
|
|||||||
using System.Buffers.Binary;
|
using System.Buffers.Binary;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
|
using System.Net.NetworkInformation;
|
||||||
|
|
||||||
namespace linker.plugins.tuntap.vea
|
namespace linker.plugins.tuntap.vea
|
||||||
{
|
{
|
||||||
@@ -173,24 +174,14 @@ namespace linker.plugins.tuntap.vea
|
|||||||
{
|
{
|
||||||
for (int i = 0; i < 10; i++)
|
for (int i = 0; i < 10; i++)
|
||||||
{
|
{
|
||||||
string output = CommandHelper.Windows(string.Empty, new string[] { "route print" });
|
NetworkInterface adapter = NetworkInterface.GetAllNetworkInterfaces()
|
||||||
if (output.Contains("IPv4") == false)
|
.FirstOrDefault(c => c.Name == InterfaceName);
|
||||||
|
if (adapter != null)
|
||||||
{
|
{
|
||||||
if (LoggerHelper.Instance.LoggerLevel <= LoggerTypes.DEBUG)
|
interfaceNumber = adapter.GetIPProperties().GetIPv4Properties().Index;
|
||||||
{
|
return true;
|
||||||
Error = $"route command not found";
|
|
||||||
LoggerHelper.Instance.Error(Error);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
foreach (var item in output.Split(Environment.NewLine))
|
|
||||||
{
|
|
||||||
if (item.Contains("WireGuard Tunnel"))
|
|
||||||
{
|
|
||||||
interfaceNumber = int.Parse(item.Substring(0, item.IndexOf('.')).Trim());
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await Task.Delay(1000).ConfigureAwait(false);
|
await Task.Delay(1000).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
if (LoggerHelper.Instance.LoggerLevel <= LoggerTypes.DEBUG)
|
if (LoggerHelper.Instance.LoggerLevel <= LoggerTypes.DEBUG)
|
||||||
|
@@ -5,6 +5,7 @@ using linker.client.capi;
|
|||||||
using linker.config;
|
using linker.config;
|
||||||
using linker.plugins.updater.messenger;
|
using linker.plugins.updater.messenger;
|
||||||
using MemoryPack;
|
using MemoryPack;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
|
||||||
namespace linker.plugins.updater
|
namespace linker.plugins.updater
|
||||||
{
|
{
|
||||||
@@ -23,22 +24,38 @@ namespace linker.plugins.updater
|
|||||||
this.config = config;
|
this.config = config;
|
||||||
}
|
}
|
||||||
|
|
||||||
public UpdateInfo Get(ApiControllerParamsInfo param)
|
public ConcurrentDictionary<string, UpdateInfo> Get(ApiControllerParamsInfo param)
|
||||||
{
|
{
|
||||||
return updaterTransfer.Get();
|
return updaterTransfer.Get();
|
||||||
}
|
}
|
||||||
public async Task Update(ApiControllerParamsInfo param)
|
public async Task Confirm(ApiControllerParamsInfo param)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(param.Content) || param.Content == config.Data.Client.Id)
|
if (string.IsNullOrWhiteSpace(param.Content) || param.Content == config.Data.Client.Id)
|
||||||
{
|
{
|
||||||
updaterTransfer.Update();
|
updaterTransfer.Confirm();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
await messengerSender.SendOnly(new MessageRequestWrap
|
await messengerSender.SendOnly(new MessageRequestWrap
|
||||||
{
|
{
|
||||||
Connection = clientSignInState.Connection,
|
Connection = clientSignInState.Connection,
|
||||||
MessengerId = (ushort)UpdaterMessengerIds.UpdateForward,
|
MessengerId = (ushort)UpdaterMessengerIds.ConfirmForward,
|
||||||
|
Payload = MemoryPackSerializer.Serialize(param.Content)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public async Task Exit(ApiControllerParamsInfo param)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(param.Content) || param.Content == config.Data.Client.Id)
|
||||||
|
{
|
||||||
|
updaterTransfer.Exit();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await messengerSender.SendOnly(new MessageRequestWrap
|
||||||
|
{
|
||||||
|
Connection = clientSignInState.Connection,
|
||||||
|
MessengerId = (ushort)UpdaterMessengerIds.ExitForward,
|
||||||
Payload = MemoryPackSerializer.Serialize(param.Content)
|
Payload = MemoryPackSerializer.Serialize(param.Content)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@@ -1,73 +1,332 @@
|
|||||||
using linker.config;
|
using linker.client;
|
||||||
|
using linker.config;
|
||||||
using linker.libs;
|
using linker.libs;
|
||||||
using System.Diagnostics;
|
using linker.plugins.updater.messenger;
|
||||||
|
using linker.server;
|
||||||
|
using MemoryPack;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.IO.Compression;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
namespace linker.plugins.updater
|
namespace linker.plugins.updater
|
||||||
{
|
{
|
||||||
public sealed class UpdaterTransfer
|
public sealed class UpdaterTransfer
|
||||||
{
|
{
|
||||||
private UpdateInfo updateInfo;
|
private UpdateInfo updateInfo = new UpdateInfo();
|
||||||
private string rootPath = "./updater";
|
private ConcurrentDictionary<string, UpdateInfo> updateInfos = new ConcurrentDictionary<string, UpdateInfo>();
|
||||||
|
|
||||||
private readonly FileConfig fileConfig;
|
private readonly FileConfig fileConfig;
|
||||||
public UpdaterTransfer(FileConfig fileConfig)
|
private readonly MessengerSender messengerSender;
|
||||||
|
private readonly ClientSignInState clientSignInState;
|
||||||
|
public UpdaterTransfer(FileConfig fileConfig, MessengerSender messengerSender, ClientSignInState clientSignInState)
|
||||||
{
|
{
|
||||||
this.fileConfig = fileConfig;
|
this.fileConfig = fileConfig;
|
||||||
RestartUpdater();
|
this.messengerSender = messengerSender;
|
||||||
LoadUpdater();
|
this.clientSignInState = clientSignInState;
|
||||||
|
|
||||||
|
clientSignInState.NetworkFirstEnabledHandle += () =>
|
||||||
|
{
|
||||||
|
LoadTask();
|
||||||
|
UpdateTask();
|
||||||
|
};
|
||||||
|
|
||||||
|
ClearTempFile();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RestartUpdater()
|
/// <summary>
|
||||||
|
/// 所有客户端的更新信息
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public ConcurrentDictionary<string, UpdateInfo> Get()
|
||||||
{
|
{
|
||||||
try
|
return updateInfos;
|
||||||
{
|
|
||||||
if (OperatingSystem.IsWindows())
|
|
||||||
File.Copy("linker.updater.exe", "linker.updater.temp.exe", true);
|
|
||||||
else
|
|
||||||
File.Copy("linker.updater", "linker.updater.temp", true);
|
|
||||||
|
|
||||||
}
|
|
||||||
catch (Exception)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
foreach (var item in Process.GetProcessesByName("linker.updater.temp"))
|
|
||||||
{
|
|
||||||
item.Kill();
|
|
||||||
}
|
|
||||||
if (OperatingSystem.IsWindows())
|
|
||||||
CommandHelper.Execute("linker.updater.temp.exe", rootPath);
|
|
||||||
else
|
|
||||||
CommandHelper.Execute("linker.updater.temp", rootPath);
|
|
||||||
}
|
}
|
||||||
private void LoadUpdater()
|
/// <summary>
|
||||||
|
/// 确认更新
|
||||||
|
/// </summary>
|
||||||
|
public void Confirm()
|
||||||
{
|
{
|
||||||
try
|
Task.Run(async () =>
|
||||||
{
|
{
|
||||||
updateInfo = new UpdateInfo
|
await DownloadUpdate();
|
||||||
|
await ExtractUpdate();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 关闭程序
|
||||||
|
/// </summary>
|
||||||
|
public void Exit()
|
||||||
|
{
|
||||||
|
Environment.Exit(1);
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 来自别的客户端的更新信息
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="info"></param>
|
||||||
|
public void Update(UpdateInfo info)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(info.MachineId) == false)
|
||||||
|
{
|
||||||
|
updateInfos.AddOrUpdate(info.MachineId, info, (a, b) => info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateTask()
|
||||||
|
{
|
||||||
|
Task.Run(async () =>
|
||||||
|
{
|
||||||
|
while (true)
|
||||||
{
|
{
|
||||||
Msg = File.ReadAllText(Path.Join(rootPath, "msg.txt")),
|
if (updateInfo.StatusChanged())
|
||||||
Version = File.ReadAllText(Path.Join(rootPath, "version.txt"))
|
{
|
||||||
};
|
updateInfo.MachineId = fileConfig.Data.Client.Id;
|
||||||
|
await messengerSender.SendOnly(new MessageRequestWrap
|
||||||
|
{
|
||||||
|
Connection = clientSignInState.Connection,
|
||||||
|
MessengerId = (ushort)UpdaterMessengerIds.UpdateForward,
|
||||||
|
Payload = MemoryPackSerializer.Serialize(updateInfo),
|
||||||
|
});
|
||||||
|
Update(updateInfo);
|
||||||
|
}
|
||||||
|
await Task.Delay(1000);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
private void LoadTask()
|
||||||
|
{
|
||||||
|
Task.Run(async () =>
|
||||||
|
{
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
await GetUpdateInfo();
|
||||||
|
await Task.Delay(60000);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task GetUpdateInfo()
|
||||||
|
{
|
||||||
|
//正在检查,或者已经确认更新了
|
||||||
|
if (updateInfo.Status == UpdateStatus.Checking || updateInfo.Status > UpdateStatus.Checked)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateStatus status = updateInfo.Status;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
updateInfo.Status = UpdateStatus.Checking;
|
||||||
|
|
||||||
|
using HttpClient httpClient = new HttpClient();
|
||||||
|
string str = await httpClient.GetStringAsync("http://gh.snltty.com:1808/https://github.com/snltty/linker/releases/latest").WaitAsync(TimeSpan.FromSeconds(15));
|
||||||
|
|
||||||
|
Match match = new Regex(@"/snltty/linker/tree/(v[\d.]+)").Match(str);
|
||||||
|
string tag = match.Groups[1].Value;
|
||||||
|
string[] msg = new Regex(@"<li>(.+)</li>").Matches(str).Select(c => c.Groups[1].Value).ToArray();
|
||||||
|
|
||||||
|
str = await httpClient.GetStringAsync($"http://gh.snltty.com:1808/https://github.com/snltty/linker/releases/expanded_assets/{tag}").WaitAsync(TimeSpan.FromSeconds(15));
|
||||||
|
string[] urls = new Regex(@"/snltty/linker/releases/(.+)\.zip").Matches(str)
|
||||||
|
.Select(c => $"http://gh.snltty.com:1808/https://github.com{c.Groups[0].Value}").ToArray();
|
||||||
|
|
||||||
|
string system = OperatingSystem.IsWindows() ? "win" : OperatingSystem.IsLinux() ? "linux" : "osx";
|
||||||
|
string arch = RuntimeInformation.ProcessArchitecture.ToString().ToLower();
|
||||||
|
|
||||||
|
updateInfo.Msg = msg;
|
||||||
|
updateInfo.Url = urls.FirstOrDefault(c => c.Contains($"linker-{system}-{arch}.zip"));
|
||||||
|
updateInfo.Version = tag;
|
||||||
|
|
||||||
|
updateInfo.Status = UpdateStatus.Checked;
|
||||||
}
|
}
|
||||||
catch (Exception)
|
catch (Exception)
|
||||||
{
|
{
|
||||||
|
updateInfo.Status = status;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private async Task ExtractUpdate()
|
||||||
|
{
|
||||||
|
//没下载完成
|
||||||
|
if (updateInfo.Status != UpdateStatus.Downloaded)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateStatus status = updateInfo.Status;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
updateInfo.Status = UpdateStatus.Extracting;
|
||||||
|
updateInfo.Current = 0;
|
||||||
|
updateInfo.Length = 0;
|
||||||
|
|
||||||
|
using ZipArchive archive = ZipFile.OpenRead("updater.zip");
|
||||||
|
updateInfo.Length = archive.Entries.Sum(c => c.Length);
|
||||||
|
|
||||||
|
string configPath = Path.GetFullPath("./configs");
|
||||||
|
|
||||||
|
foreach (ZipArchiveEntry entry in archive.Entries)
|
||||||
|
{
|
||||||
|
string entryPath = Path.GetFullPath(Path.Join("./", entry.FullName.Substring(entry.FullName.IndexOf('/'))));
|
||||||
|
if (entryPath.EndsWith('\\') || entryPath.EndsWith('/'))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (entryPath.StartsWith(configPath))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Directory.Exists(Path.GetDirectoryName(entryPath)) == false)
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(Path.GetDirectoryName(entryPath));
|
||||||
|
}
|
||||||
|
if (File.Exists(entryPath))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
File.Move(entryPath, $"{entryPath}.temp", true);
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
using Stream entryStream = entry.Open();
|
||||||
|
using FileStream fileStream = File.Create(entryPath);
|
||||||
|
byte[] buffer = new byte[4096];
|
||||||
|
int bytesRead;
|
||||||
|
while ((bytesRead = await entryStream.ReadAsync(buffer)) != 0)
|
||||||
|
{
|
||||||
|
await fileStream.WriteAsync(buffer.AsMemory(0, bytesRead));
|
||||||
|
updateInfo.Current += bytesRead;
|
||||||
|
}
|
||||||
|
|
||||||
|
entryStream.Dispose();
|
||||||
|
|
||||||
|
fileStream.Flush();
|
||||||
|
fileStream.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
archive.Dispose();
|
||||||
|
File.Delete("updater.zip");
|
||||||
|
|
||||||
|
updateInfo.Status = UpdateStatus.Extracted;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
if (LoggerHelper.Instance.LoggerLevel <= LoggerTypes.DEBUG)
|
||||||
|
{
|
||||||
|
LoggerHelper.Instance.Error(ex);
|
||||||
|
}
|
||||||
|
updateInfo.Status = status;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private async Task DownloadUpdate()
|
||||||
|
{
|
||||||
|
|
||||||
|
if (updateInfo.Status != UpdateStatus.Checked)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
UpdateStatus status = updateInfo.Status;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
updateInfo.Status = UpdateStatus.Downloading;
|
||||||
|
updateInfo.Current = 0;
|
||||||
|
updateInfo.Length = 0;
|
||||||
|
|
||||||
|
using HttpClient httpClient = new HttpClient();
|
||||||
|
using HttpResponseMessage response = await httpClient.GetAsync(updateInfo.Url, HttpCompletionOption.ResponseHeadersRead);
|
||||||
|
response.EnsureSuccessStatusCode();
|
||||||
|
updateInfo.Length = response.Content.Headers.ContentLength ?? 0;
|
||||||
|
using Stream contentStream = await response.Content.ReadAsStreamAsync();
|
||||||
|
|
||||||
|
|
||||||
|
using FileStream fileStream = new FileStream("updater.zip", FileMode.OpenOrCreate, FileAccess.ReadWrite);
|
||||||
|
byte[] buffer = new byte[4096];
|
||||||
|
int readBytes = 0;
|
||||||
|
while ((readBytes = await contentStream.ReadAsync(buffer)) != 0)
|
||||||
|
{
|
||||||
|
await fileStream.WriteAsync(buffer.AsMemory(0, readBytes));
|
||||||
|
updateInfo.Current += readBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateInfo.Status = UpdateStatus.Downloaded;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
if (LoggerHelper.Instance.LoggerLevel <= LoggerTypes.DEBUG)
|
||||||
|
{
|
||||||
|
LoggerHelper.Instance.Error(ex);
|
||||||
|
}
|
||||||
|
try
|
||||||
|
{
|
||||||
|
File.Delete("updater.zip");
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
updateInfo.Status = status;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public UpdateInfo Get()
|
private void ClearTempFile(string path = "./")
|
||||||
{
|
{
|
||||||
LoadUpdater();
|
string fullPath = Path.GetFullPath(path);
|
||||||
return updateInfo;
|
|
||||||
|
foreach (var item in Directory.GetFiles(fullPath).Where(c=>c.EndsWith(".temp")))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
File.Delete(item);
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foreach (var item in Directory.GetDirectories(fullPath))
|
||||||
|
{
|
||||||
|
ClearTempFile(item);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
public void Update()
|
|
||||||
|
}
|
||||||
|
|
||||||
|
[MemoryPackable]
|
||||||
|
public sealed partial class UpdateInfo
|
||||||
|
{
|
||||||
|
[MemoryPackIgnore]
|
||||||
|
public string Version { get; set; }
|
||||||
|
[MemoryPackIgnore]
|
||||||
|
public string[] Msg { get; set; }
|
||||||
|
[MemoryPackIgnore]
|
||||||
|
public string Url { get; set; }
|
||||||
|
|
||||||
|
public string MachineId { get; set; }
|
||||||
|
public UpdateStatus Status { get; set; } = UpdateStatus.None;
|
||||||
|
public long Length { get; set; }
|
||||||
|
public long Current { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
private int statusCode = 0;
|
||||||
|
public bool StatusChanged()
|
||||||
{
|
{
|
||||||
File.WriteAllText(Path.Join(rootPath, "extract.txt"), $"{fileConfig.Data.Client.Updater.RunCommand}{Environment.NewLine}{fileConfig.Data.Client.Updater.StopCommand}");
|
int code = (byte)Status ^ Length.GetHashCode() ^ Current.GetHashCode();
|
||||||
|
|
||||||
|
bool res = statusCode != code;
|
||||||
|
statusCode = code;
|
||||||
|
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed class UpdateInfo
|
public enum UpdateStatus : byte
|
||||||
{
|
{
|
||||||
public string Version { get; set; }
|
None = 0,
|
||||||
public string Msg { get; set; }
|
Checking = 1,
|
||||||
|
Checked = 2,
|
||||||
|
Downloading = 3,
|
||||||
|
Downloaded = 4,
|
||||||
|
Extracting = 5,
|
||||||
|
Extracted = 6
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -4,41 +4,7 @@ namespace linker.plugins.updater.config
|
|||||||
{
|
{
|
||||||
public sealed class UpdaterConfigInfo
|
public sealed class UpdaterConfigInfo
|
||||||
{
|
{
|
||||||
private string runWindows = "sc start linker.service";
|
|
||||||
private string stopWindows = "sc stop linker.service & taskkill /F /IM linker.exe & taskkill /F /IM linker.tray.win.exe";
|
|
||||||
|
|
||||||
private string runLinux = "systemctl start linker";
|
|
||||||
private string stopLinux = "systemctl stop linker";
|
|
||||||
|
|
||||||
private string runOsx = "launchctl start linker";
|
|
||||||
private string stopOsx = "launchctl stop linker";
|
|
||||||
|
|
||||||
|
|
||||||
private string runCommand = string.Empty;
|
|
||||||
public string RunCommand
|
|
||||||
{
|
|
||||||
get => runCommand; set
|
|
||||||
{
|
|
||||||
runCommand = value;
|
|
||||||
if (string.IsNullOrWhiteSpace(runCommand))
|
|
||||||
{
|
|
||||||
runCommand = OperatingSystem.IsWindows() ? runWindows : OperatingSystem.IsLinux() ? runLinux : runOsx;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private string stopCommand = string.Empty;
|
|
||||||
public string StopCommand
|
|
||||||
{
|
|
||||||
get => stopCommand; set
|
|
||||||
{
|
|
||||||
stopCommand = value;
|
|
||||||
if (string.IsNullOrWhiteSpace(stopCommand))
|
|
||||||
{
|
|
||||||
stopCommand = OperatingSystem.IsWindows() ? stopWindows : OperatingSystem.IsLinux() ? stopLinux : stopOsx;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
using linker.plugins.signin.messenger;
|
using linker.plugins.signin.messenger;
|
||||||
using linker.server;
|
using linker.server;
|
||||||
|
using MemoryPack;
|
||||||
|
|
||||||
namespace linker.plugins.updater.messenger
|
namespace linker.plugins.updater.messenger
|
||||||
{
|
{
|
||||||
@@ -12,13 +13,33 @@ namespace linker.plugins.updater.messenger
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 更新
|
/// 确认更新
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="connection"></param>
|
||||||
|
[MessengerId((ushort)UpdaterMessengerIds.Confirm)]
|
||||||
|
public void Confirm(IConnection connection)
|
||||||
|
{
|
||||||
|
updaterTransfer.Confirm();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 更新信息
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="connection"></param>
|
/// <param name="connection"></param>
|
||||||
[MessengerId((ushort)UpdaterMessengerIds.Update)]
|
[MessengerId((ushort)UpdaterMessengerIds.Update)]
|
||||||
public void Update(IConnection connection)
|
public void Update(IConnection connection)
|
||||||
{
|
{
|
||||||
updaterTransfer.Update();
|
UpdateInfo info = MemoryPackSerializer.Deserialize<UpdateInfo>(connection.ReceiveRequestWrap.Payload.Span);
|
||||||
|
updaterTransfer.Update(info);
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 关闭信息
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="connection"></param>
|
||||||
|
[MessengerId((ushort)UpdaterMessengerIds.Exit)]
|
||||||
|
public void Exit(IConnection connection)
|
||||||
|
{
|
||||||
|
updaterTransfer.Exit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35,24 +56,62 @@ namespace linker.plugins.updater.messenger
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 广播更新消息
|
/// 转发确认更新消息
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="connection"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
[MessengerId((ushort)UpdaterMessengerIds.ConfirmForward)]
|
||||||
|
public async Task ConfirmForward(IConnection connection)
|
||||||
|
{
|
||||||
|
string machineId = MemoryPackSerializer.Deserialize<string>(connection.ReceiveRequestWrap.Payload.Span);
|
||||||
|
if (signCaching.TryGet(connection.Id, out SignCacheInfo cache) && signCaching.TryGet(machineId, out SignCacheInfo cache1) && cache.GroupId == cache1.GroupId)
|
||||||
|
{
|
||||||
|
await messengerSender.SendOnly(new MessageRequestWrap
|
||||||
|
{
|
||||||
|
Connection = cache1.Connection,
|
||||||
|
MessengerId = (ushort)UpdaterMessengerIds.Confirm
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 转发更新消息
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="connection"></param>
|
/// <param name="connection"></param>
|
||||||
[MessengerId((ushort)UpdaterMessengerIds.UpdateForward)]
|
[MessengerId((ushort)UpdaterMessengerIds.UpdateForward)]
|
||||||
public void UpdateForward(IConnection connection)
|
public void UpdateForward(IConnection connection)
|
||||||
{
|
{
|
||||||
|
UpdateInfo info = MemoryPackSerializer.Deserialize<UpdateInfo>(connection.ReceiveRequestWrap.Payload.Span);
|
||||||
if (signCaching.TryGet(connection.Id, out SignCacheInfo cache))
|
if (signCaching.TryGet(connection.Id, out SignCacheInfo cache))
|
||||||
{
|
{
|
||||||
List<SignCacheInfo> caches = signCaching.Get(cache.GroupId);
|
foreach (var item in signCaching.Get(cache.GroupId).Where(c => c.Connected && c.MachineId != connection.Id))
|
||||||
foreach (SignCacheInfo item in caches.Where(c => c.MachineId != connection.Id && c.Connected))
|
|
||||||
{
|
{
|
||||||
_ = messengerSender.SendOnly(new MessageRequestWrap
|
_ = messengerSender.SendOnly(new MessageRequestWrap
|
||||||
{
|
{
|
||||||
Connection = item.Connection,
|
Connection = item.Connection,
|
||||||
MessengerId = (ushort)UpdaterMessengerIds.Update,
|
MessengerId = (ushort)UpdaterMessengerIds.Update,
|
||||||
Timeout = 1000,
|
Payload = connection.ReceiveRequestWrap.Payload
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 转发关闭消息
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="connection"></param>
|
||||||
|
[MessengerId((ushort)UpdaterMessengerIds.ExitForward)]
|
||||||
|
public async Task ExitForward(IConnection connection)
|
||||||
|
{
|
||||||
|
string machineId = MemoryPackSerializer.Deserialize<string>(connection.ReceiveRequestWrap.Payload.Span);
|
||||||
|
if (signCaching.TryGet(connection.Id, out SignCacheInfo cache) && signCaching.TryGet(machineId, out SignCacheInfo cache1) && cache.GroupId == cache1.GroupId)
|
||||||
|
{
|
||||||
|
await messengerSender.SendOnly(new MessageRequestWrap
|
||||||
|
{
|
||||||
|
Connection = cache1.Connection,
|
||||||
|
MessengerId = (ushort)UpdaterMessengerIds.Exit
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -7,6 +7,12 @@
|
|||||||
UpdateForward = 2601,
|
UpdateForward = 2601,
|
||||||
Update = 2602,
|
Update = 2602,
|
||||||
|
|
||||||
|
ConfirmForward = 2603,
|
||||||
|
Confirm = 2604,
|
||||||
|
|
||||||
|
ExitForward = 2605,
|
||||||
|
Exit = 2606,
|
||||||
|
|
||||||
Max = 2299
|
Max = 2299
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -27,8 +27,6 @@ for %%r in (win-x64,win-arm64) do (
|
|||||||
|
|
||||||
for %%r in (win-x64,win-arm64,linux-x64,linux-arm64,osx-x64,osx-arm64) do (
|
for %%r in (win-x64,win-arm64,linux-x64,linux-arm64,osx-x64,osx-arm64) do (
|
||||||
|
|
||||||
rem dotnet publish ./linker.updater -c release -f net8.0 -o public/publish/%%r/linker-%%r/ -r %%r -p:PublishSingleFile=true -p:PublishTrimmed=true --self-contained true -p:TrimMode=partial -p:TieredPGO=true -p:DebugType=none -p:DebugSymbols=false -p:EnableCompressionInSingleFile=true -p:DebuggerSupport=false -p:EnableUnsafeBinaryFormatterSerialization=false -p:EnableUnsafeUTF7Encoding=false -p:HttpActivityPropagationSupport=false -p:InvariantGlobalization=true -p:MetadataUpdaterSupport=false -p:UseSystemResourceKeys=true
|
|
||||||
|
|
||||||
dotnet publish ./linker -c release -f net8.0 -o ./public/publish/%%r/linker-%%r -r %%r -p:PublishSingleFile=true -p:PublishTrimmed=true --self-contained true -p:TrimMode=partial -p:TieredPGO=true -p:DebugType=none -p:DebugSymbols=false -p:EnableCompressionInSingleFile=true -p:DebuggerSupport=false -p:EnableUnsafeBinaryFormatterSerialization=false -p:EnableUnsafeUTF7Encoding=false -p:HttpActivityPropagationSupport=false -p:InvariantGlobalization=true -p:MetadataUpdaterSupport=false -p:UseSystemResourceKeys=true
|
dotnet publish ./linker -c release -f net8.0 -o ./public/publish/%%r/linker-%%r -r %%r -p:PublishSingleFile=true -p:PublishTrimmed=true --self-contained true -p:TrimMode=partial -p:TieredPGO=true -p:DebugType=none -p:DebugSymbols=false -p:EnableCompressionInSingleFile=true -p:DebuggerSupport=false -p:EnableUnsafeBinaryFormatterSerialization=false -p:EnableUnsafeUTF7Encoding=false -p:HttpActivityPropagationSupport=false -p:InvariantGlobalization=true -p:MetadataUpdaterSupport=false -p:UseSystemResourceKeys=true
|
||||||
echo F|xcopy "public\\extends\\%%r\\linker-%%r\\*" "public\\publish\\%%r\\linker-%%r\\*" /s /f /h /y
|
echo F|xcopy "public\\extends\\%%r\\linker-%%r\\*" "public\\publish\\%%r\\linker-%%r\\*" /s /f /h /y
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user