18 Commits

Author SHA1 Message Date
乔焰阳
e856543529 Simplify release workflow by removing setup-go step
Removed setup-go step as Go environment is provided by the container image.
2025-11-06 15:50:06 +08:00
乔焰阳
4bed53dc16 Downgrade GitHub actions to v3 2025-11-04 16:12:48 +08:00
14f98a6009 去掉测试代码 2025-11-03 20:51:34 +08:00
610ae3aab2 github更新库修复无法对比版本的问题 2025-11-03 12:00:23 +08:00
4c4db5e9e2 服务端节点会给客户端追加公网ip 2025-10-31 17:51:20 +08:00
01cd7ae6e3 增加关闭窗口不退出程序 2025-10-31 17:34:38 +08:00
8d41c1ba62 实现了控制台启动后自动隐藏 2025-10-31 17:29:14 +08:00
22b4402737 去掉本地的更新逻辑,是有分离后的更新库 2025-10-31 15:50:59 +08:00
8b943b0cca 版本更新改为协程,预防拥堵主程序 2025-10-31 14:29:07 +08:00
03f762e910 更新完后需要修改文件可执行权限 2025-10-31 10:05:10 +08:00
84723248cd 删除更新的临时压缩包 2025-10-30 19:35:21 +08:00
d3f3687201 使用新的解压targz压缩的方法 2025-10-30 19:34:28 +08:00
63f5bef038 补充仓库缺少的基础配置文件 2025-10-30 18:49:13 +08:00
f90cf8b855 升级后删除旧的压缩包,修改版本对比文件 2025-10-30 18:24:08 +08:00
a0f367efeb 每天凌晨自动检测更新 2025-10-30 18:19:02 +08:00
ceb44e936f linux使用软重启,预防卡进程 2025-10-30 18:16:14 +08:00
49c84b886b 修复解压缩zip的错误 2025-10-30 17:58:49 +08:00
607af816c4 更新不同系统的解压方式,区分zip与gzip 2025-10-30 17:47:28 +08:00
22 changed files with 252 additions and 658 deletions

View File

@@ -14,15 +14,18 @@ permissions:
jobs:
build:
# runs-on指定了运行作业的虚拟机环境类型
# 即使使用containerruns-on仍然是必要的配置
runs-on: ubuntu-latest
# 在runner上运行golang容器
container:
# 使用最新版本的golang镜像无需指定具体版本号
image: golang:latest-alpine
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: '1.24.x'
# Go environment is already provided by the container image
- name: Install gf CLI
run: |
@@ -39,7 +42,7 @@ jobs:
run: gf build -ew -v "${{ github.ref_name }}"
- name: Upload build artifacts
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v3
with:
name: p2p-${{ github.ref_name }}
path: |
@@ -47,13 +50,19 @@ jobs:
release:
needs: build
# runs-on指定了运行作业的虚拟机环境类型
# 即使使用containerruns-on仍然是必要的配置
runs-on: ubuntu-latest
# 在runner上运行golang容器
container:
# 使用最新版本的golang镜像无需指定具体版本号
image: golang:latest-alpine
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Download artifacts
uses: actions/download-artifact@v4
uses: actions/download-artifact@v3
with:
name: p2p-${{ github.ref_name }}
path: bin

3
.gitignore vendored
View File

@@ -16,8 +16,9 @@ manifest/output/
temp/
temp.yaml
bin
**/config/config.yaml
v1.0.0/
config/local.yaml
main.exe~
download/
version.txt
internal/packed/build_pack_data.go

View File

@@ -1,11 +1 @@
package v1
import "github.com/gogf/gf/v2/frame/g"
type UpdateReq struct {
g.Meta `path:"/system/update" tags:"system" method:"get" sm:"更新服务端"`
Url string `json:"url" dc:"更新地址"`
Version string `json:"version" dc:"当前版本"`
}
type UpdateRes struct {
}

View File

@@ -1,50 +1,42 @@
package main
import (
"image"
"os"
"github.com/icza/icox" // 替换为icox库
"github.com/nfnt/resize"
)
func main() {
// 1. 读取PNG文件
inputPath := "manifest/images/logo.png" // 输入PNG路径
outputPath := "manifest/images/favicon.ico" // 输出ICO路径
pngFile, err := os.Open(inputPath)
if err != nil {
panic("无法打开PNG文件: " + err.Error())
}
defer pngFile.Close()
// 2. 解码PNG为image.Image对象
img, _, err := image.Decode(pngFile)
if err != nil {
panic("PNG解码失败: " + err.Error())
}
// 3. 定义ICO需要包含的尺寸常见尺寸
sizes := []uint{16, 32, 64, 128} // 可根据需求添加更多尺寸
var icoImages []image.Image
// 4. 缩放图像到每个目标尺寸并收集
for _, size := range sizes {
// 使用Lanczos3算法缩放高质量
resized := resize.Resize(size, size, img, resize.Lanczos3)
icoImages = append(icoImages, resized)
}
// 5. 编码为ICO并写入文件关键修改使用icox.Encode
icoFile, err := os.Create(outputPath)
if err != nil {
panic("无法创建ICO文件: " + err.Error())
}
defer icoFile.Close()
// icox.Encode直接支持多尺寸切片[]image.Image
if err := icox.Encode(icoFile, icoImages); err != nil {
panic("ICO编码失败: " + err.Error())
}
//// 1. 读取PNG文件
//inputPath := "manifest/images/logo.png" // 输入PNG路径
//outputPath := "manifest/images/favicon.ico" // 输出ICO路径
//
//pngFile, err := os.Open(inputPath)
//if err != nil {
// panic("无法打开PNG文件: " + err.Error())
//}
//defer pngFile.Close()
//
//// 2. 解码PNG为image.Image对象
//img, _, err := image.Decode(pngFile)
//if err != nil {
// panic("PNG解码失败: " + err.Error())
//}
//
//// 3. 定义ICO需要包含的尺寸常见尺寸
//sizes := []uint{16, 32, 64, 128} // 可根据需求添加更多尺寸
//var icoImages []image.Image
//
//// 4. 缩放图像到每个目标尺寸并收集
//for _, size := range sizes {
// // 使用Lanczos3算法缩放高质量
// resized := resize.Resize(size, size, img, resize.Lanczos3)
// icoImages = append(icoImages, resized)
//}
//
//// 5. 编码为ICO并写入文件关键修改使用icox.Encode
//icoFile, err := os.Create(outputPath)
//if err != nil {
// panic("无法创建ICO文件: " + err.Error())
//}
//defer icoFile.Close()
//
//// icox.Encode直接支持多尺寸切片[]image.Image
//if err := icox.Encode(icoFile, icoImages); err != nil {
// panic("ICO编码失败: " + err.Error())
//}
}

18
go.mod
View File

@@ -1,19 +1,19 @@
module github.com/ayflying/p2p
go 1.24.0
go 1.24.8
toolchain go1.24.9
require (
github.com/Kodeworks/golang-image-ico v0.0.0-20141118225523-73f0f4cfade9
github.com/ayflying/update-github-release v0.0.4
github.com/dop251/goja v0.0.0-20250630131328-58d95d85e994
github.com/getlantern/systray v1.2.2
github.com/gogf/gf/contrib/nosql/redis/v2 v2.9.3
github.com/gogf/gf/v2 v2.9.3
github.com/google/uuid v1.6.0
github.com/gogf/gf/v2 v2.9.4
github.com/gorilla/websocket v1.5.3
github.com/libp2p/go-libp2p v0.43.0
github.com/libp2p/go-libp2p-kad-dht v0.35.1
github.com/minio/minio-go/v7 v7.0.95
github.com/multiformats/go-multiaddr v0.16.1
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646
)
require (
@@ -24,7 +24,6 @@ require (
github.com/clbanning/mxj/v2 v2.7.0 // indirect
github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/dlclark/regexp2 v1.11.4 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/emirpasic/gods v1.18.1 // indirect
@@ -47,6 +46,7 @@ require (
github.com/goccy/go-json v0.10.5 // indirect
github.com/google/gopacket v1.1.19 // indirect
github.com/google/pprof v0.0.0-20230405160723-4a4c7d95572b // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/grokify/html-strip-tags-go v0.1.0 // indirect
github.com/hashicorp/golang-lru v1.0.2 // indirect
github.com/huin/goupnp v1.3.0 // indirect
@@ -81,7 +81,6 @@ require (
github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc // indirect
github.com/minio/crc64nvme v1.0.2 // indirect
github.com/minio/md5-simd v1.1.2 // indirect
github.com/minio/minio-go/v7 v7.0.95 // indirect
github.com/minio/sha256-simd v1.0.1 // indirect
github.com/mr-tron/base58 v1.2.0 // indirect
github.com/multiformats/go-base32 v0.1.0 // indirect
@@ -97,7 +96,7 @@ require (
github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6 // indirect
github.com/olekukonko/errors v1.1.0 // indirect
github.com/olekukonko/ll v0.1.1 // indirect
github.com/olekukonko/tablewriter v1.0.9 // indirect
github.com/olekukonko/tablewriter v1.1.0 // indirect
github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c // indirect
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect
github.com/philhofer/fwd v1.2.0 // indirect
@@ -128,7 +127,6 @@ require (
github.com/quic-go/qpack v0.5.1 // indirect
github.com/quic-go/quic-go v0.54.0 // indirect
github.com/quic-go/webtransport-go v0.9.0 // indirect
github.com/redis/go-redis/v9 v9.12.1 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/rs/xid v1.6.0 // indirect
github.com/spaolacci/murmur3 v1.1.0 // indirect

24
go.sum
View File

@@ -10,21 +10,17 @@ git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGy
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg=
github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
github.com/Kodeworks/golang-image-ico v0.0.0-20141118225523-73f0f4cfade9 h1:1ltqoej5GtaWF8jaiA49HwsZD459jqm9YFz9ZtMFpQA=
github.com/Kodeworks/golang-image-ico v0.0.0-20141118225523-73f0f4cfade9/go.mod h1:7uhhqiBaR4CpN0k9rMjOtjpcfGd6DG2m04zQxKnWQ0I=
github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0=
github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
github.com/ayflying/update-github-release v0.0.4 h1:iJ09jieBFW/t96N3NqcFA6tS7hxKx4fnETp5VC7jSbo=
github.com/ayflying/update-github-release v0.0.4/go.mod h1:9ctXuagiEKABWLaS4ocRCUcnN77jdYm7zir7NQ+rIeM=
github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o=
github.com/benbjohnson/clock v1.3.5/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g=
github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
@@ -42,8 +38,6 @@ github.com/decred/dcrd/crypto/blake256 v1.1.0 h1:zPMNGQCm0g4QTY27fOCorQW7EryeQ/U
github.com/decred/dcrd/crypto/blake256 v1.1.0/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 h1:NMZiJj8QnKe1LgsbDayM4UoHwbvwDRwnI3hwNaAHRnc=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0/go.mod h1:ZXNYxsqcloTdSy/rNShjYzMhyjf0LaoftYK0p+A3h40=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/dlclark/regexp2 v1.11.4 h1:rPYF9/LECdNymJufQKmri9gV604RvvABwgOA8un7yAo=
github.com/dlclark/regexp2 v1.11.4/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
github.com/dop251/goja v0.0.0-20250630131328-58d95d85e994 h1:aQYWswi+hRL2zJqGacdCZx32XjKYV8ApXFGntw79XAM=
@@ -98,10 +92,8 @@ github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/me
github.com/go-yaml/yaml v2.1.0+incompatible/go.mod h1:w2MrLa16VYP0jy6N7M5kHaCkaLENm+P+Tv+MfurjSw0=
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/gogf/gf/contrib/nosql/redis/v2 v2.9.3 h1:VTbeHq8XpBCWFIwBGmuBl+jP8AepULnpgNz8GPBKBRQ=
github.com/gogf/gf/contrib/nosql/redis/v2 v2.9.3/go.mod h1:gcidgAYn4IWbx08QUThg7jw6bz3KklXI9/5zg8jnVHY=
github.com/gogf/gf/v2 v2.9.3 h1:qjN4s55FfUzxZ1AE8vUHNDX3V0eIOUGXhF2DjRTVZQ4=
github.com/gogf/gf/v2 v2.9.3/go.mod h1:w6rcfD13SmO7FKI80k9LSLiSMGqpMYp50Nfkrrc2sEE=
github.com/gogf/gf/v2 v2.9.4 h1:6vleEWypot9WBPncP2GjbpgAUeG6Mzb1YESb9nPMkjY=
github.com/gogf/gf/v2 v2.9.4/go.mod h1:Ukl+5HUH9S7puBmNLR4L1zUqeRwi0nrW4OigOknEztU=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
@@ -273,16 +265,14 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo=
github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM=
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6 h1:zrbMGy9YXpIeTnGj4EljqMiZsIcE09mmF8XsD5AYOJc=
github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6/go.mod h1:rEKTHC9roVVicUIfZK7DYrdIoM0EOr8mK1Hj5s3JjH0=
github.com/olekukonko/errors v1.1.0 h1:RNuGIh15QdDenh+hNvKrJkmxxjV4hcS50Db478Ou5sM=
github.com/olekukonko/errors v1.1.0/go.mod h1:ppzxA5jBKcO1vIpCXQ9ZqgDh8iwODz6OXIGKU8r5m4Y=
github.com/olekukonko/ll v0.1.1 h1:9Dfeed5/Mgaxb9lHRAftLK9pVfYETvHn+If6lywVhJc=
github.com/olekukonko/ll v0.1.1/go.mod h1:2dJo+hYZcJMLMbKwHEWvxCUbAOLc/CXWS9noET22Mdo=
github.com/olekukonko/tablewriter v1.0.9 h1:XGwRsYLC2bY7bNd93Dk51bcPZksWZmLYuaTHR0FqfL8=
github.com/olekukonko/tablewriter v1.0.9/go.mod h1:5c+EBPeSqvXnLLgkm9isDdzR3wjfBkHR9Nhfp3NWrzo=
github.com/olekukonko/tablewriter v1.1.0 h1:N0LHrshF4T39KvI96fn6GT8HEjXRXYNDrDjKFDB7RIY=
github.com/olekukonko/tablewriter v1.1.0/go.mod h1:5c+EBPeSqvXnLLgkm9isDdzR3wjfBkHR9Nhfp3NWrzo=
github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8=
github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c h1:rp5dCmg/yLR3mgFuSOe4oEnDDmGLROTvMragMUXpTQw=
github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c/go.mod h1:X07ZCGwUbLaax7L0S3Tw4hpejzu63ZrrQiUe6W0hcy0=
@@ -355,8 +345,6 @@ github.com/quic-go/quic-go v0.54.0 h1:6s1YB9QotYI6Ospeiguknbp2Znb/jZYjZLRXn9kMQB
github.com/quic-go/quic-go v0.54.0/go.mod h1:e68ZEaCdyviluZmy44P6Iey98v/Wfz6HCjQEm+l8zTY=
github.com/quic-go/webtransport-go v0.9.0 h1:jgys+7/wm6JarGDrW+lD/r9BGqBAmqY/ssklE09bA70=
github.com/quic-go/webtransport-go v0.9.0/go.mod h1:4FUYIiUc75XSsF6HShcLeXXYZJ9AGwo/xh3L8M/P1ao=
github.com/redis/go-redis/v9 v9.12.1 h1:k5iquqv27aBtnTm2tIkROUDp8JBXhXZIVu1InSgvovg=
github.com/redis/go-redis/v9 v9.12.1/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q5lHuCH/Iw=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=

36
internal/boot/boot.go Normal file
View File

@@ -0,0 +1,36 @@
package boot
import (
"context"
updateGithub "github.com/ayflying/update-github-release"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gcron"
"github.com/gogf/gf/v2/os/gctx"
)
func Boot() {
getDev, _ := g.Cfg().GetWithEnv(gctx.New(), "dev")
if !getDev.Bool() {
var update = updateGithub.New("https://api.github.com/repos/ayflying/p2p/releases/latest")
// 每天0点检查更新
gcron.Add(gctx.New(), "0 0 0 * * *", func(ctx context.Context) {
err := update.CheckUpdate(true)
if err != nil {
g.Log().Errorf(ctx, "检查更新失败:%v", err)
}
})
go func() {
//在协程中检查更新,预防主程序阻塞
err := update.CheckUpdate(true)
if err != nil {
g.Log().Errorf(gctx.New(), "检查更新失败:%v", err)
}
}()
} else {
g.Log().Debugf(gctx.New(), "开发模式,不检查更新")
}
}

View File

@@ -5,9 +5,9 @@ import (
"fmt"
"time"
"github.com/ayflying/p2p/internal/boot"
"github.com/ayflying/p2p/internal/consts"
"github.com/ayflying/p2p/internal/controller/p2p"
"github.com/ayflying/p2p/internal/controller/system"
"github.com/ayflying/p2p/internal/service"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/ghttp"
@@ -18,7 +18,7 @@ import (
)
func init() {
err := Main.AddCommand(&Main, &Debug, &Update)
err := Main.AddCommand(&Main, &Debug)
if err != nil {
g.Log().Error(gctx.GetInitCtx(), err)
return
@@ -32,6 +32,10 @@ var (
Brief: "start http server",
Func: func(ctx context.Context, parser *gcmd.Parser) (err error) {
g.Log().Debug(ctx, "开始执行main")
// 初始化服务器
boot.Boot()
Version, err := g.Cfg("hack").Get(ctx, "gfcli.build.version")
g.Log().Debugf(ctx, "当前启动的版本为:%v", Version)
@@ -69,7 +73,6 @@ var (
group.Middleware(ghttp.MiddlewareHandlerResponse)
group.Bind(
p2p.NewV1(),
system.NewV1(),
)
})

View File

@@ -1,109 +0,0 @@
package cmd
import (
"bytes"
"context"
"fmt"
"path"
"runtime"
systemV1 "github.com/ayflying/p2p/api/system/v1"
"github.com/ayflying/p2p/internal/service"
"github.com/gogf/gf/v2/crypto/gsha1"
"github.com/gogf/gf/v2/encoding/gcompress"
"github.com/gogf/gf/v2/encoding/gjson"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gcfg"
"github.com/gogf/gf/v2/os/gcmd"
"github.com/gogf/gf/v2/os/gfile"
)
var (
Update = gcmd.Command{
Name: "update",
Usage: "update",
Brief: "更新版本",
Func: func(ctx context.Context, parser *gcmd.Parser) (err error) {
g.Log().Info(ctx, "准备上传更新文件")
//加载编辑配置文件
g.Cfg("hack").GetAdapter().(*gcfg.AdapterFile).SetFileName("hack/config.yaml")
//获取文件名
getName, err := g.Cfg("hack").Get(ctx, "gfcli.build.name")
name := getName.String()
getPath, err := g.Cfg("hack").Get(ctx, "gfcli.build.path")
pathMain := getPath.String()
//获取版本号
getVersion, err := g.Cfg("hack").Get(ctx, "gfcli.build.version")
version := getVersion.String()
// 拼接操作系统和架构格式OS_ARCH
platform := fmt.Sprintf("%s_%s", runtime.GOOS, runtime.GOARCH)
rootDir := "server_update"
var versionFile = make(map[string]string)
var filePath = path.Join(pathMain, version, platform, name)
dirList, _ := gfile.ScanDir(path.Join(pathMain, version), "*", false)
for _, v := range dirList {
updatePlatform := gfile.Name(v)
updateFilePath := path.Join(rootDir, name, version, updatePlatform)
var obj bytes.Buffer
g.Log().Debugf(ctx, "读取目录成功:%v", v)
fileMian := path.Join(v, name)
g.Log().Debugf(ctx, "判断当前文件是否存在:%v", fileMian)
if gfile.IsFile(fileMian) {
// 写入文件哈希
versionFile[updatePlatform] = gsha1.MustEncryptFile(fileMian)
err = gcompress.GzipPathWriter(fileMian, &obj)
service.S3().PutObject(ctx, &obj, updateFilePath+".gz")
g.Log().Debugf(ctx, "成功上传文件到:%v", updateFilePath+".gz")
}
if gfile.IsFile(fileMian + ".exe") {
// 写入文件哈希
versionFile[updatePlatform] = gsha1.MustEncryptFile(fileMian + ".exe")
err = gcompress.GzipPathWriter(fileMian+".exe", &obj)
service.S3().PutObject(ctx, &obj, updateFilePath+".gz")
g.Log().Debugf(ctx, "成功上传文件到:%v", updateFilePath+".gz")
}
// 写入文件版本文件
fileByte := gjson.MustEncode(versionFile)
service.S3().PutObject(ctx, bytes.NewReader(fileByte), path.Join(rootDir, name, "version.json"))
if err != nil {
g.Log().Error(ctx, err)
}
}
g.Log().Debugf(ctx, "当前获取到的地址为:%v", filePath)
versionUrl := service.S3().GetCdnUrl(path.Join(rootDir, name))
listVar := g.Cfg().MustGet(ctx, "message.list")
var p2pItem []struct {
Host string `json:"host"`
Port int `json:"port"`
SSL bool `json:"ssl"`
Ws string `json:"ws"`
}
listVar.Scan(&p2pItem)
for _, v := range p2pItem {
url := "http"
if v.SSL == true {
url = "https"
}
url = fmt.Sprintf("%s://%s:%d/system/update", url, v.Host, v.Port)
g.Log().Debugf(ctx, "开始上传到服务器:%v,file=%v", url, versionUrl)
_, err := g.Client().Get(ctx, url, systemV1.UpdateReq{
Url: versionUrl,
Version: version,
})
if err != nil {
g.Log().Error(ctx, err)
}
}
return
}}
)

View File

@@ -4,12 +4,4 @@
package system
import (
"github.com/ayflying/p2p/api/system"
)
type ControllerV1 struct{}
func NewV1() system.ISystemV1 {
return &ControllerV1{}
}

View File

@@ -1,70 +0,0 @@
package system
import (
"context"
"net/url"
"path"
"github.com/ayflying/p2p/api/system/v1"
"github.com/ayflying/p2p/internal/service"
"github.com/gogf/gf/v2/crypto/gsha1"
"github.com/gogf/gf/v2/encoding/gjson"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gcmd"
"github.com/gogf/gf/v2/os/gfile"
)
func (c *ControllerV1) Update(ctx context.Context, req *v1.UpdateReq) (res *v1.UpdateRes, err error) {
getRunFile := gcmd.GetArg(0).String()
fileSha, err := gsha1.EncryptFile(getRunFile)
g.Log().Debugf(ctx, "当前文件哈希值:%v", fileSha)
versionUrl, _ := url.JoinPath(req.Url, "version.json")
resp, err := g.Client().Get(ctx, versionUrl)
var version map[string]string
gjson.DecodeTo(resp.ReadAll(), &version)
for k, _ := range version {
//downloadUrl, _ := url.QueryUnescape(v)
downloadUrl, _ := url.JoinPath(req.Url, req.Version, k+".gz")
fileByte, err2 := g.Client().Get(ctx, downloadUrl)
if err2 != nil {
g.Log().Error(ctx, err2)
continue
}
putFile := path.Join("download", gfile.Basename(downloadUrl))
err2 = gfile.PutBytes(putFile, fileByte.ReadAll())
if err2 != nil {
g.Log().Error(ctx, err2)
continue
}
}
type DataType struct {
File []byte `json:"file"`
Name string `json:"name"`
}
//var GatewayMessage *message.GatewayMessage
var msgData = struct {
Files []*DataType `json:"files"`
}{}
msgData.Files = []*DataType{}
files, _ := gfile.ScanDir("download", "*.gz")
for _, v := range files {
msgData.Files = append(msgData.Files, &DataType{
File: gfile.GetBytes(v),
Name: gfile.Basename(v),
})
}
service.P2P().SendAll("update", msgData)
//更新自己的文件
//err = service.System().Update(ctx)
return
}

View File

@@ -12,6 +12,7 @@ import (
"github.com/gogf/gf/v2/os/gcmd"
"github.com/gogf/gf/v2/os/gctx"
"github.com/gogf/gf/v2/os/gfile"
"github.com/gogf/gf/v2/os/gres"
)
// 引入 Windows API 函数
@@ -20,9 +21,20 @@ var (
kernel32 = syscall.NewLazyDLL("kernel32.dll")
showWindow = user32.NewProc("ShowWindow")
getConsoleWnd = kernel32.NewProc("GetConsoleWindow")
freeConsole = kernel32.NewProc("FreeConsole")
attachConsole = kernel32.NewProc("AttachConsole")
allocConsole = kernel32.NewProc("AllocConsole")
setConsoleCtrlHandler = kernel32.NewProc("SetConsoleCtrlHandler")
consoleCtrlHandler uintptr
)
const (
ctrlCloseEvent = 2
)
func (s *sOS) start() {
// 注册控制台关闭事件处理:点击叉叉仅隐藏控制台而不退出程序
s.setupConsoleCloseHandler()
// 系统托盘初始化(设置图标、右键菜单)
go systray.Run(s.onSystrayReady, s.onSystrayExit)
@@ -30,9 +42,21 @@ func (s *sOS) start() {
// 系统托盘初始化(设置图标、右键菜单)
func (s *sOS) onSystrayReady() {
//s.hideConsole()
// s.hideConsole()
var iconByte []byte
if !gfile.Exists(s.systray.Icon) {
iconByte = gres.GetContent(s.systray.Icon)
gfile.PutBytes(s.systray.Icon, iconByte)
}
iconByte = gfile.GetBytes(s.systray.Icon)
//if gres.Contains(s.systray.Icon) {
// iconByte = gres.GetContent(s.systray.Icon)
// gfile.PutBytes(s.systray.Icon, iconByte)
//} else {
// iconByte = gfile.GetBytes(s.systray.Icon)
//}
iconByte := gfile.GetBytes(s.systray.Icon)
systray.SetIcon(iconByte)
systray.SetTitle(s.systray.Title)
systray.SetTooltip(s.systray.Tooltip)
@@ -46,6 +70,7 @@ func (s *sOS) onSystrayReady() {
select {
case <-mQuit.ClickedCh:
systray.Quit()
case <-mShow.ClickedCh:
// 显示窗口
s.showConsole()
@@ -77,13 +102,12 @@ func (s *sOS) update(version, server string) {
// 隐藏控制台窗口
func (s *sOS) hideConsole() {
// 获取当前控制台窗口句柄
// 仅隐藏控制台窗口(保留现有缓冲区以便后续显示时保留历史日志)
hWnd, _, _ := getConsoleWnd.Call()
if hWnd == 0 {
return // 无控制台窗口如编译为GUI子系统时
}
if hWnd != 0 {
// SW_HIDE = 0隐藏窗口
showWindow.Call(hWnd, 0)
}
}
// 显示控制台窗口
@@ -91,8 +115,40 @@ func (s *sOS) showConsole() {
// 获取当前控制台窗口句柄
hWnd, _, _ := getConsoleWnd.Call()
if hWnd == 0 {
return
// 如果当前进程没有控制台,尝试附加到父进程控制台
// ATTACH_PARENT_PROCESS = (DWORD)-1
ret, _, _ := attachConsole.Call(uintptr(^uint32(0)))
if ret == 0 {
// 附加失败则分配一个新的控制台窗口
allocConsole.Call()
}
// 重新获取控制台窗口句柄
hWnd, _, _ = getConsoleWnd.Call()
}
if hWnd != 0 {
// SW_SHOW = 5显示窗口
showWindow.Call(hWnd, 5)
}
}
// 注册控制台关闭事件处理器,将关闭事件转换为隐藏行为
func (s *sOS) setupConsoleCloseHandler() {
if consoleCtrlHandler != 0 {
return
}
consoleCtrlHandler = syscall.NewCallback(func(ctrlType uint32) uintptr {
if ctrlType == ctrlCloseEvent {
// 用户点击控制台窗口的关闭按钮(X):仅隐藏,不退出
hWnd, _, _ := getConsoleWnd.Call()
if hWnd != 0 {
// SW_HIDE = 0
showWindow.Call(hWnd, 0)
}
// 返回 TRUE 表示事件已处理,阻止默认终止行为
return 1
}
// 其他事件交由系统默认处理
return 0
})
setConsoleCtrlHandler.Call(consoleCtrlHandler, uintptr(1))
}

View File

@@ -178,6 +178,7 @@ func (s *sP2P) register() error {
addrs[i] = addr.String()
}
ports, _ := s.getLocalTCPPorts(s.client.host)
// 构建注册消息
msg := GatewayMessage{
Type: MsgTypeRegister,
@@ -185,6 +186,7 @@ func (s *sP2P) register() error {
Data: gjson.MustEncode(g.Map{
"peer_id": s.client.host.ID().String(),
"addrs": addrs,
"ports": ports,
}),
}
@@ -456,7 +458,7 @@ func (s *sP2P) receiveGatewayMessages(ctx context.Context) {
g.Log().Info(ctx, "文件接收完成")
// 开始覆盖文件与重启
err = service.System().Update(ctx)
err = service.System().Update(ctx, "")
//// 调用不同系统的更新服务
//service.OS().Update(msgData.Version, msgData.Server)

View File

@@ -4,6 +4,7 @@ import (
"context"
"encoding/json"
"fmt"
"net"
"strings"
"time"
@@ -104,6 +105,7 @@ func (s *sP2P) handleRegister(ctx context.Context, conn *websocket.Conn, msg *Ga
var data struct {
PeerID string `json:"peer_id"`
Addrs []string `json:"addrs"`
Ports []int `json:"ports"`
}
if err := json.Unmarshal(msg.Data, &data); err != nil {
@@ -111,18 +113,20 @@ func (s *sP2P) handleRegister(ctx context.Context, conn *websocket.Conn, msg *Ga
return
}
//// 追加公网ip
//publicIp, _, _ := net.SplitHostPort(conn.RemoteAddr().String())
//ParseIP := net.ParseIP(publicIp)
//var ipType string
//if ParseIP.To4() != nil {
// ipType = "ip4"
//} else {
// ipType = "ip6"
//}
// 追加公网ip
publicIp, _, _ := net.SplitHostPort(conn.RemoteAddr().String())
ParseIP := net.ParseIP(publicIp)
var ipType string
if ParseIP.To4() != nil {
ipType = "ip4"
} else {
ipType = "ip6"
}
//port2 := 53533
//data.Addrs = append(data.Addrs, fmt.Sprintf("/%s/%s/tcp/%d", ipType, publicIp, port2))
//data.Addrs = append(data.Addrs, fmt.Sprintf("/%s/%s/udp/%d/quic-v1", ipType, publicIp, port2))
for _, port2 := range data.Ports {
data.Addrs = append(data.Addrs, fmt.Sprintf("/%s/%s/tcp/%d", ipType, publicIp, port2))
data.Addrs = append(data.Addrs, fmt.Sprintf("/%s/%s/udp/%d/quic-v1", ipType, publicIp, port2))
}
// 过滤回环地址
data.Addrs = s.filterLoopbackAddrs(data.Addrs)

View File

@@ -6,7 +6,6 @@ import (
"fmt"
"math/rand"
"net"
"net/http"
"strings"
"sync"
"time"
@@ -80,46 +79,6 @@ func init() {
ip, _ = service.P2P().GetIPv4PublicIP()
}
// 获取公网IP并判断类型ipv4/ipv6
func (s *sP2P) getPublicIPAndType() (ip string, ipType string, err error) {
// 公网IP查询接口多个备用
client := http.Client{Timeout: 5 * time.Second}
for _, api := range ipAPIs {
resp, err := client.Get(api)
if err != nil {
continue
}
defer resp.Body.Close()
// 读取响应纯IP字符串
buf := make([]byte, 128)
n, err := resp.Body.Read(buf)
if err != nil {
continue
}
ip = strings.TrimSpace(string(buf[:n]))
if ip == "" {
continue
}
// 判断IP类型
parsedIP := net.ParseIP(ip)
if parsedIP == nil {
continue // 无效IP格式
}
if parsedIP.To4() != nil {
return ip, "ipv4", nil // IPv4
} else if parsedIP.To16() != nil {
return ip, "ipv6", nil // IPv6
}
}
return "", "", fmt.Errorf("所有公网IP查询接口均失败")
}
// 只获取IPv4公网IP过滤IPv6结果
func (s *sP2P) GetIPv4PublicIP() (string, error) {
ctx := gctx.New()
@@ -188,9 +147,10 @@ func (s *sP2P) removeDuplicates(strs []string) []string {
return result
}
const privKeyPath = "runtime/p2p.key"
// 生成固定密钥(核心:通过固定种子生成相同密钥)
func (s *sP2P) generateFixedKey() (crypto.PrivKey, error) {
privKeyPath := "runtime/message.key"
if ok := gfile.Exists(privKeyPath); ok {
// 从文件读取密钥
keyBytes := gfile.GetBytes(privKeyPath)

View File

@@ -1,87 +0,0 @@
package system
import "time"
type T struct {
Url string `json:"url"`
AssetsUrl string `json:"assets_url"`
UploadUrl string `json:"upload_url"`
HtmlUrl string `json:"html_url"`
Id int `json:"id"`
Author *Author `json:"author"`
NodeId string `json:"node_id"`
TagName string `json:"tag_name"`
TargetCommitish string `json:"target_commitish"`
Name string `json:"name"`
Draft bool `json:"draft"`
Immutable bool `json:"immutable"`
Prerelease bool `json:"prerelease"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
PublishedAt time.Time `json:"published_at"`
Assets []*Assets
TarballUrl string `json:"tarball_url"`
ZipballUrl string `json:"zipball_url"`
Body string `json:"body"`
}
type Author struct {
Login string `json:"login"`
Id int `json:"id"`
NodeId string `json:"node_id"`
AvatarUrl string `json:"avatar_url"`
GravatarId string `json:"gravatar_id"`
Url string `json:"url"`
HtmlUrl string `json:"html_url"`
FollowersUrl string `json:"followers_url"`
FollowingUrl string `json:"following_url"`
GistsUrl string `json:"gists_url"`
StarredUrl string `json:"starred_url"`
SubscriptionsUrl string `json:"subscriptions_url"`
OrganizationsUrl string `json:"organizations_url"`
ReposUrl string `json:"repos_url"`
EventsUrl string `json:"events_url"`
ReceivedEventsUrl string `json:"received_events_url"`
Type string `json:"type"`
UserViewType string `json:"user_view_type"`
SiteAdmin bool `json:"site_admin"`
}
type Assets struct {
Url string `json:"url"`
Id int `json:"id"`
NodeId string `json:"node_id"`
Name string `json:"name"`
Label string `json:"label"`
Uploader *Uploader `json:"uploader"`
ContentType string `json:"content_type"`
State string `json:"state"`
Size int `json:"size"`
Digest string `json:"digest"`
DownloadCount int `json:"download_count"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
BrowserDownloadUrl string `json:"browser_download_url"`
}
type Uploader struct {
Login string `json:"login"`
Id int `json:"id"`
NodeId string `json:"node_id"`
AvatarUrl string `json:"avatar_url"`
GravatarId string `json:"gravatar_id"`
Url string `json:"url"`
HtmlUrl string `json:"html_url"`
FollowersUrl string `json:"followers_url"`
FollowingUrl string `json:"following_url"`
GistsUrl string `json:"gists_url"`
StarredUrl string `json:"starred_url"`
SubscriptionsUrl string `json:"subscriptions_url"`
OrganizationsUrl string `json:"organizations_url"`
ReposUrl string `json:"repos_url"`
EventsUrl string `json:"events_url"`
ReceivedEventsUrl string `json:"received_events_url"`
Type string `json:"type"`
UserViewType string `json:"user_view_type"`
SiteAdmin bool `json:"site_admin"`
}

View File

@@ -1,11 +1,5 @@
package system
import (
"github.com/ayflying/p2p/internal/service"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gctx"
)
type sSystem struct{}
func New() *sSystem {
@@ -13,14 +7,7 @@ func New() *sSystem {
}
func init() {
service.RegisterSystem(New())
getDev, _ := g.Cfg().GetWithEnv(gctx.New(), "dev")
if !getDev.Bool() {
service.System().CheckUpdate()
} else {
g.Log().Debugf(gctx.New(), "开发模式,不检查更新")
}
}
func (system *sSystem) Init() {}
func (s *sSystem) Init() {}

View File

@@ -1,204 +0,0 @@
package system
import (
"context"
"encoding/json"
"fmt"
"log"
"net/http"
"os"
"os/exec"
"path"
"path/filepath"
"runtime"
"strings"
"time"
"github.com/ayflying/p2p/internal/service"
"github.com/gogf/gf/v2/encoding/gcompress"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gcmd"
"github.com/gogf/gf/v2/os/gctx"
"github.com/gogf/gf/v2/os/gfile"
)
// 本地版本号(建议从编译参数注入,如 -ldflags "-X main.version=v0.1.3"
var localVersion = "v0.0.0"
// 对应 GitHub API 响应的核心字段(按需精简)
type GitHubRelease struct {
Url string `json:"url"`
AssetsUrl string `json:"assets_url"`
UploadUrl string `json:"upload_url"`
HtmlUrl string `json:"html_url"`
Id int `json:"id"`
TagName string `json:"tag_name"`
Assets []*Assets `json:"assets"`
NodeId string `json:"node_id"`
TargetCommitish string `json:"target_commitish"`
Name string `json:"name"`
Draft bool `json:"draft"`
Immutable bool `json:"immutable"`
Prerelease bool `json:"prerelease"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
PublishedAt time.Time `json:"published_at"`
TarballUrl string `json:"tarball_url"`
ZipballUrl string `json:"zipball_url"`
Body string `json:"body"`
}
func (s *sSystem) Update(ctx context.Context) (err error) {
//拼接操作系统和架构格式OS_ARCH
platform := fmt.Sprintf("%s_%s", runtime.GOOS, runtime.GOARCH)
runFile := gcmd.GetArg(0).String()
oldFile, err := service.System().RenameRunningFile(runFile)
g.Log().Debugf(ctx, "执行文件改名为%v", oldFile)
gz := path.Join("download", platform+".gz")
err = gcompress.UnGzipFile(gz, runFile)
go func() {
log.Println("5秒后开始重启...")
time.Sleep(5 * time.Second)
if err = service.System().RestartSelf(); err != nil {
log.Fatalf("重启失败:%v", err)
}
}()
return
}
// RestartSelf 实现 Windows 平台下的程序自重启
func (s *sSystem) RestartSelf() error {
// 1. 获取当前程序的绝对路径
exePath, err := os.Executable()
if err != nil {
return err
}
// 处理路径中的符号链接(确保路径正确)
exePath, err = filepath.EvalSymlinks(exePath)
if err != nil {
return err
}
// 2. 获取命令行参数os.Args[0] 是程序名,实际参数从 os.Args[1:] 开始)
args := os.Args[1:]
// 3. 构建新进程命令(路径为当前程序,参数为原参数)
cmd := exec.Command(exePath, args...)
// 设置新进程的工作目录与当前进程一致
cmd.Dir, err = os.Getwd()
if err != nil {
return err
}
// 新进程的输出继承当前进程的标准输出(可选,根据需求调整)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
cmd.Stdin = os.Stdin
// 4. 启动新进程非阻塞Start() 后立即返回)
if err := cmd.Start(); err != nil {
return err
}
// 5. 新进程启动成功后,退出当前进程
os.Exit(0)
return nil // 理论上不会执行到这里
}
// RenameRunningFile 重命名正在运行的程序文件(如 message.exe → message.exe~
func (s *sSystem) RenameRunningFile(exePath string) (string, error) {
// 目标备份文件名message.exe → message.exe~
backupPath := exePath + "~"
// 先删除已存在的备份文件(若有)
if _, err := os.Stat(backupPath); err == nil {
if err := os.Remove(backupPath); err != nil {
return "", fmt.Errorf("删除旧备份文件失败: %v", err)
}
}
// 重命名正在运行的 exe 文件
// 关键Windows 允许对锁定的文件执行重命名操作
if err := os.Rename(exePath, backupPath); err != nil {
return "", fmt.Errorf("重命名运行中文件失败: %v", err)
}
return backupPath, nil
}
// 简化版版本对比(仅适用于 vX.Y.Z 格式)
func (s *sSystem) isNewVersion(local, latest string) bool {
// 移除前缀 "v",按 "." 分割成数字切片
localParts := strings.Split(strings.TrimPrefix(local, "v"), ".")
latestParts := strings.Split(strings.TrimPrefix(latest, "v"), ".")
// 逐段对比版本号(如 0.1.3 vs 0.1.4 → 后者更新)
for i := 0; i < len(localParts) && i < len(latestParts); i++ {
if localParts[i] < latestParts[i] {
return true
} else if localParts[i] > latestParts[i] {
return false
}
}
// 若前缀相同,长度更长的版本更新(如 0.1 vs 0.1.1
return len(localParts) < len(latestParts)
}
func (s *sSystem) getLatestVersion() (string, []*Assets, error) {
apiURL := "https://api.github.com/repos/ayflying/p2p/releases/latest"
resp, err := http.Get(apiURL)
if err != nil {
return "", nil, fmt.Errorf("请求失败:%v", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return "", nil, fmt.Errorf("API 响应错误:%d", resp.StatusCode)
}
var release GitHubRelease
if err := json.NewDecoder(resp.Body).Decode(&release); err != nil {
return "", nil, fmt.Errorf("解析响应失败:%v", err)
}
return release.TagName, release.Assets, nil
}
func (s *sSystem) CheckUpdate() {
ctx := gctx.New()
latestVersion, assets, err := s.getLatestVersion()
if err != nil {
fmt.Printf("检查更新失败:%v\n", err)
return
}
localVersion = gfile.GetContents("download/version.txt")
if s.isNewVersion(localVersion, latestVersion) {
g.Log().Printf(ctx, "发现新版本:%s当前版本%s", latestVersion, localVersion)
//拼接操作系统和架构格式OS_ARCH
platform := fmt.Sprintf("%s_%s", runtime.GOOS, runtime.GOARCH)
//name := fmt.Sprintf("p2p_%s_%s.tar.gz", latestVersion, platform)
fmt.Println("下载链接:")
for _, asset := range assets {
if strings.Contains(fmt.Sprintf("_%s.", asset.Name), platform) {
fmt.Printf("- %s\n", asset.BrowserDownloadUrl)
fileDownload, err2 := g.Client().Get(ctx, asset.BrowserDownloadUrl)
if err2 != nil {
return
}
//filename := gfile.Name()
err = gfile.PutBytes(path.Join("download", asset.Name), fileDownload.ReadAll())
// 保存最新版本号到文件
gfile.PutContents("download/version.txt", latestVersion)
break
}
}
} else {
fmt.Printf("当前已是最新版本:%s\n", localVersion)
}
}

View File

@@ -1 +1,2 @@
package packed

View File

@@ -12,12 +12,12 @@ import (
type (
ISystem interface {
Init()
Update(ctx context.Context) (err error)
Update(ctx context.Context, gzFile string) (err error)
// RestartSelf 实现 Windows 平台下的程序自重启
RestartSelf() error
// RenameRunningFile 重命名正在运行的程序文件(如 message.exe → message.exe~
RenameRunningFile(exePath string) (string, error)
CheckUpdate()
CheckUpdate() (err error)
}
)

View File

@@ -10,9 +10,8 @@ import (
"github.com/gogf/gf/v2/os/gcmd"
"github.com/gogf/gf/v2/os/gfile"
"github.com/gogf/gf/v2/os/gtime"
//步骤1加载驱动
_ "github.com/gogf/gf/contrib/nosql/redis/v2"
//_ "github.com/gogf/gf/contrib/nosql/redis/v2"
"github.com/ayflying/p2p/internal/cmd"
"github.com/gogf/gf/v2/os/gctx"
@@ -24,7 +23,7 @@ var (
func main() {
g.Log().Infof(ctx, "启动文件最后修改时间:%v", gtime.New(gfile.MTime(gcmd.GetArg(0).String())).String())
g.Dump("v1.0.0.2")
//g.Dump("v1.0.0.2")
if ok := gfile.Exists("runtime"); !ok {
gfile.Mkdir("runtime")

View File

@@ -0,0 +1,46 @@
module:
server: true
client: true
# https://goframe.org/docs/web/server-config-file-template
server:
address: "51888"
# openapiPath: "/api.json"
# swaggerPath: "/swagger"
dumpRouterMap: false
graceful: true
# https://goframe.org/docs/core/glog-config
logger:
level : "all"
stdout: true
# https://goframe.org/docs/core/gdb-config-file
database:
default:
link: "mysql:root:12345678@tcp(127.0.0.1:3306)/test"
redis:
default:
address: "ay.cname.com:6379"
db: 15
pass: "12345678"
cache:
address: "ay.cname.com:6379"
db: 15
pass: "12345678"
p2p:
list:
- host: "host.yunloli.cn"
port: 51888
ssl: false
ws: ws
- host: "ay.cname.com"
port: 51888
ssl: false
ws: ws