Skip to content

Commit 5b5fc8d

Browse files
committed
added update
1 parent 22d811a commit 5b5fc8d

File tree

12 files changed

+205
-100
lines changed

12 files changed

+205
-100
lines changed

package.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "@conet.project/conet-proxy",
33

4-
"version": "0.13.0",
4+
"version": "0.13.2",
55

66
"license": "UNLICENSED",
77
"files": [
@@ -32,7 +32,9 @@
3232
"ip": "^2.0.1",
3333
"ethers": "^6.13.7",
3434
"openpgp": "^5.11.2",
35-
"eth-crypto": "^2.8.0"
35+
"eth-crypto": "^2.8.0",
36+
"axios": "^1.10.0",
37+
"unzipper": "^0.12.3"
3638
},
3739
"devDependencies": {
3840
"@types/async": "^3.2.24",

src/localServer/localServer.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,7 @@ import {ethers} from 'ethers'
1313
import * as openpgp from 'openpgp'
1414
import os from 'node:os'
1515
import CONET_Guardian_NodeInfo_ABI from './CONET_Guardian_NodeInfo_ABI.json'
16-
17-
16+
import {runUpdater} from './updateProcess'
1817

1918

2019
const ver = '0.1.4'
@@ -177,6 +176,7 @@ const startSilentPass = (vpnObj: Native_StartVPNObj) => {
177176
logger(inspect(vpnObj, false, 3, true))
178177

179178
_proxyServer = new proxyServer((3002).toString(), vpnObj.entryNodes, vpnObj.exitNode, vpnObj.privateKey, true, '')
179+
runUpdater(vpnObj.entryNodes)
180180
return true
181181
}
182182

@@ -264,7 +264,13 @@ export class Daemon {
264264
app.use ( express.static ( staticFolder ))
265265
//app.use ( express.static ( launcherFolder ))
266266
app.use ( express.json() )
267+
app.use (async (req, res: any, next) => {
267268

269+
logger(Colors.blue(`${req.url}`))
270+
271+
return next()
272+
273+
})
268274
app.once ( 'error', ( err: any ) => {
269275
logger (err)
270276
logger (`Local server on ERROR, try restart!`)

src/localServer/updateProcess.ts

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
import { logger } from "./logger"
2+
import {join} from 'node:path'
3+
import fs from 'node:fs'
4+
import * as unzipper from 'unzipper'
5+
import http from 'node:http'
6+
import currentVer from './workers/update.json'
7+
import { inspect } from "node:util"
8+
// 定义 update.json 的数据结构
9+
interface UpdateInfo {
10+
ver: string
11+
filename: string
12+
}
13+
const MAX_REDIRECTS = 5 // 防止无限重定向
14+
/**
15+
* 辅助函数:下载文件并流式解压到指定路径
16+
* @param downloadUrl 文件的URL
17+
* @param extractPath 解压的目标路径
18+
* @param redirectCount 当前重定向次数
19+
*/
20+
const downloadAndUnzip = (downloadUrl: string, extractPath: string, redirectCount = 0): Promise<void> => {
21+
return new Promise((resolve, reject) => {
22+
if (redirectCount > MAX_REDIRECTS) {
23+
return reject(new Error('超过最大重定向次数'))
24+
}
25+
const options = { agent: httpAgent }
26+
http.get(downloadUrl, options, (response) => {
27+
// 处理重定向
28+
if (response.statusCode && response.statusCode >= 300 && response.statusCode < 400 && response.headers.location) {
29+
logger(`下载被重定向到: ${response.headers.location}`)
30+
return downloadAndUnzip(response.headers.location, extractPath, redirectCount + 1).then(resolve).catch(reject)
31+
}
32+
33+
if (response.statusCode !== 200) {
34+
return reject(new Error(`下载失败,状态码: ${response.statusCode}`))
35+
}
36+
37+
// 将下载流直接导入解压器
38+
response.pipe(unzipper.Extract({ path: extractPath }))
39+
.on('finish', resolve)
40+
.on('error', reject)
41+
}).on('error', (err) => {
42+
logger(`downloadAndUnzip Error`, err.message)
43+
})
44+
})
45+
}
46+
const httpAgent = new http.Agent({ keepAlive: false })
47+
48+
/**
49+
* 辅助函数:发起 GET 请求,处理重定向,并以文本形式返回响应体。
50+
* @param requestUrl 请求的 URL
51+
* @param redirectCount 当前重定向次数
52+
* @returns Promise,解析为响应体字符串
53+
*/
54+
const fetchText = (requestUrl: string, redirectCount = 0): Promise<string> => {
55+
return new Promise((resolve, reject) => {
56+
if (redirectCount > MAX_REDIRECTS) {
57+
return reject(new Error('超过最大重定向次数'))
58+
}
59+
logger(`fetchText access ${requestUrl}`)
60+
const options = { agent: httpAgent }
61+
http.get(requestUrl, options, (response) => {
62+
// 处理重定向
63+
if (response.statusCode && response.statusCode >= 300 && response.statusCode < 400 && response.headers.location) {
64+
logger(`被重定向到: ${response.headers.location}`)
65+
return fetchText(response.headers.location, redirectCount + 1).then(resolve).catch(reject)
66+
}
67+
68+
// 处理请求错误
69+
if (response.statusCode && response.statusCode >= 400) {
70+
return reject(new Error(`请求失败,状态码: ${response.statusCode}`))
71+
}
72+
logger(`fetchText success!`)
73+
let body = ''
74+
response.setEncoding('utf8')
75+
response.on('data', (chunk) => { body += chunk; })
76+
response.on('end', () => {
77+
logger(`response.on('end') ${body}`)
78+
resolve(body)
79+
})
80+
}).on('error', (err) => {
81+
logger(`fetchText Error`, err.message)
82+
})
83+
})
84+
}
85+
86+
/**
87+
* 从节点列表中随机选择一个节点
88+
* @param nodes 节点信息数组
89+
* @returns 随机选中的一个节点信息对象
90+
*/
91+
92+
const getRandomNode = (nodes: nodes_info[]): nodes_info => {
93+
if (!nodes || nodes.length === 0) {
94+
throw new Error('节点列表为空,无法选择节点。')
95+
}
96+
const randomIndex = Math.floor(Math.random() * nodes.length)
97+
return nodes[randomIndex]
98+
}
99+
100+
/**
101+
* 主更新函数
102+
*/
103+
export const runUpdater = async (nodes: nodes_info[] ) => {
104+
105+
106+
logger('🚀 开始执行动态节点更新程序...')
107+
108+
try {
109+
110+
111+
const selectedNode = getRandomNode(nodes)
112+
logger(`✅ 节点列表获取成功!已随机选择节点: ${selectedNode.ip_addr} (位于 ${selectedNode.region})`);
113+
114+
// --- 步骤 2: 使用选定节点的 IP 获取更新信息 ---
115+
// 使用节点的 IP 地址构建 API 的基础 URL
116+
const baseApiUrl = `http://${selectedNode.ip_addr}/silentpass-rpc/`
117+
const updateJsonUrl = `${baseApiUrl}update.json`
118+
119+
// --- 步骤 2: 使用选定节点的 IP 获取更新信息 ---
120+
121+
const updateInfoText = await fetchText(updateJsonUrl)
122+
logger(`正在从选定节点 ${updateJsonUrl} 获取更新信息... updateInfoText = ${updateInfoText}`)
123+
const updateInfo: UpdateInfo = JSON.parse(updateInfoText)
124+
125+
126+
if (!updateInfo.filename) {
127+
throw new Error('无法从 JSON 响应中获取文件名。')
128+
}
129+
130+
logger(`✅ 获取信息成功!最新版本: ${updateInfo.ver}, 文件名: ${updateInfo.filename}`)
131+
132+
const updateVer = isNewerVersion(currentVer.ver, updateInfo.ver)
133+
if (!updateVer) {
134+
return logger(`runUpdater [No update]!`)
135+
}
136+
// --- 步骤 3: 从选定节点下载并解压文件 ---
137+
const downloadUrl = `${baseApiUrl}${updateInfo.filename}`
138+
const extractPath = join(__dirname, 'workers')
139+
140+
logger(`⏳ 正在从 ${downloadUrl} 下载并解压...`)
141+
logger(`将解压到目录: ${extractPath}`)
142+
143+
// 确保目标目录存在
144+
if (!fs.existsSync(extractPath)) {
145+
fs.mkdirSync(extractPath, { recursive: true })
146+
}
147+
await downloadAndUnzip(downloadUrl, extractPath)
148+
logger(`🎉 成功下载并解压文件到 ${extractPath}`)
149+
150+
} catch (error) {
151+
console.error('❌ 更新过程中发生错误:', error instanceof Error ? error.message : error)
152+
}
153+
}
154+
// 执行更新程序
155+
156+
/**
157+
* 比较两个语义化版本号。
158+
* @param oldVer 旧版本号,如 "0.18.0"
159+
* @param newVer 新版本号,如 "0.18.1"
160+
* @returns 如果 newVer 比 oldVer 新,则返回 true;否则返回 false。
161+
*/
162+
function isNewerVersion(oldVer: string, newVer: string): boolean {
163+
const oldParts = oldVer.split('.').map(Number);
164+
const newParts = newVer.split('.').map(Number);
165+
166+
for (let i = 0; i < oldParts.length; i++) {
167+
if (newParts[i] > oldParts[i]) {
168+
return true;
169+
}
170+
if (newParts[i] < oldParts[i]) {
171+
return false;
172+
}
173+
}
174+
175+
return false; // 如果版本号完全相同,则不是更新的版本
176+
}
177+

src/localServer/workers/asset-manifest.json

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
{
22
"files": {
33
"main.css": "/static/css/main.b71dcddc.css",
4-
"main.js": "/static/js/main.a340fe9c.js",
4+
"main.js": "/static/js/main.6e3cb4ef.js",
55
"static/js/453.a5cbc0be.chunk.js": "/static/js/453.a5cbc0be.chunk.js",
6-
"service-worker.js": "/service-worker.js",
76
"static/media/GC.svg": "/static/media/GC.32c345f58a5986b6be47a893faa2ccfc.svg",
87
"static/media/sp-token.svg": "/static/media/sp-token.01da7809f36c2002dfb6fd98f3827a44.svg",
98
"static/media/main-wallet.svg": "/static/media/main-wallet.282cf0fe43d81965df2ba5c4ebd19566.svg",
@@ -49,11 +48,11 @@
4948
"static/media/home-icon-grey.svg": "/static/media/home-icon-grey.7c0953e4572ebe8dd813c4a3cd870b10.svg",
5049
"static/media/split-tunneling.svg": "/static/media/split-tunneling.9e6f6574614b30a8fa5483b6e26b49af.svg",
5150
"main.b71dcddc.css.map": "/static/css/main.b71dcddc.css.map",
52-
"main.a340fe9c.js.map": "/static/js/main.a340fe9c.js.map",
51+
"main.6e3cb4ef.js.map": "/static/js/main.6e3cb4ef.js.map",
5352
"453.a5cbc0be.chunk.js.map": "/static/js/453.a5cbc0be.chunk.js.map"
5453
},
5554
"entrypoints": [
5655
"static/css/main.b71dcddc.css",
57-
"static/js/main.a340fe9c.js"
56+
"static/js/main.6e3cb4ef.js"
5857
]
5958
}

src/localServer/workers/index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="/favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no"><meta name="theme-color" content="#000000"/><meta name="description" content="Web site created using create-react-app"/><link rel="apple-touch-icon" href="/logo192.png"/><link rel="manifest" href="/manifest.json"/><title>Silent Pass</title><script type="text/javascript">window.Comm100API=window.Comm100API||{},function(t){function e(e){var c=document.createElement("script"),d=document.getElementsByTagName("script")[0];c.type="text/javascript",c.async=!0,c.src=e+t.site_id,d.parentNode.insertBefore(c,d)}t.chat_buttons=t.chat_buttons||[],t.chat_buttons.push({code_plan:"efd822ce-7299-4fda-9fc1-252dd2f01fc5",hide:!0}),t.site_id=90007504,t.main_code_plan="efd822ce-7299-4fda-9fc1-252dd2f01fc5",e("https://vue.comm100.com/livechat.ashx?siteId="),setTimeout((function(){t.loaded||e("https://standby.comm100vue.com/livechat.ashx?siteId=")}),5e3)}(window.Comm100API||{})</script><script defer="defer" src="/static/js/main.a340fe9c.js"></script><link href="/static/css/main.b71dcddc.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>
1+
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="/favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no"><meta name="theme-color" content="#000000"/><meta name="description" content="Web site created using create-react-app"/><link rel="apple-touch-icon" href="/logo192.png"/><link rel="manifest" href="/manifest.json"/><title>Silent Pass</title><script type="text/javascript">window.Comm100API=window.Comm100API||{},function(t){function e(e){var c=document.createElement("script"),d=document.getElementsByTagName("script")[0];c.type="text/javascript",c.async=!0,c.src=e+t.site_id,d.parentNode.insertBefore(c,d)}t.chat_buttons=t.chat_buttons||[],t.chat_buttons.push({code_plan:"efd822ce-7299-4fda-9fc1-252dd2f01fc5",hide:!0}),t.site_id=90007504,t.main_code_plan="efd822ce-7299-4fda-9fc1-252dd2f01fc5",e("https://vue.comm100.com/livechat.ashx?siteId="),setTimeout((function(){t.loaded||e("https://standby.comm100vue.com/livechat.ashx?siteId=")}),5e3)}(window.Comm100API||{})</script><script defer="defer" src="/static/js/main.6e3cb4ef.js"></script><link href="/static/css/main.b71dcddc.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>

src/localServer/workers/loader.js

Lines changed: 0 additions & 82 deletions
This file was deleted.

src/localServer/workers/service-worker.js

Lines changed: 0 additions & 2 deletions
This file was deleted.

src/localServer/workers/service-worker.js.map

Lines changed: 0 additions & 1 deletion
This file was deleted.

src/localServer/workers/static/js/main.a340fe9c.js renamed to src/localServer/workers/static/js/main.6e3cb4ef.js

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/localServer/workers/static/js/main.a340fe9c.js.LICENSE.txt renamed to src/localServer/workers/static/js/main.6e3cb4ef.js.LICENSE.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313

1414
/*! MIT License. Copyright 2015-2022 Richard Moore <[email protected]>. See LICENSE.txt. */
1515

16-
/*! OpenPGP.js v6.1.0 - 2025-01-30 - this is LGPL licensed code, see LICENSE/our website https://openpgpjs.org/ for more information. */
16+
/*! OpenPGP.js v6.1.1 - 2025-05-19 - this is LGPL licensed code, see LICENSE/our website https://openpgpjs.org/ for more information. */
1717

1818
/*! ieee754. BSD-3-Clause License. Feross Aboukhadijeh <https://feross.org/opensource> */
1919

0 commit comments

Comments
 (0)