mirror of
https://github.com/ayflying/p2p.git
synced 2026-03-04 17:29:22 +00:00
Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 14f98a6009 | |||
| 610ae3aab2 | |||
| 4c4db5e9e2 | |||
| 01cd7ae6e3 | |||
| 8d41c1ba62 | |||
| 22b4402737 | |||
| 8b943b0cca | |||
| 03f762e910 | |||
| 84723248cd | |||
| d3f3687201 | |||
| 63f5bef038 | |||
| f90cf8b855 | |||
| a0f367efeb | |||
| ceb44e936f | |||
| 49c84b886b | |||
| 607af816c4 |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
}
|
||||
|
||||
@@ -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
18
go.mod
@@ -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
24
go.sum
@@ -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
36
internal/boot/boot.go
Normal 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(), "开发模式,不检查更新")
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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(),
|
||||
)
|
||||
})
|
||||
|
||||
|
||||
@@ -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
|
||||
}}
|
||||
)
|
||||
@@ -4,12 +4,4 @@
|
||||
|
||||
package system
|
||||
|
||||
import (
|
||||
"github.com/ayflying/p2p/api/system"
|
||||
)
|
||||
|
||||
type ControllerV1 struct{}
|
||||
|
||||
func NewV1() system.ISystemV1 {
|
||||
return &ControllerV1{}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -12,17 +12,29 @@ 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 函数
|
||||
var (
|
||||
user32 = syscall.NewLazyDLL("user32.dll")
|
||||
kernel32 = syscall.NewLazyDLL("kernel32.dll")
|
||||
showWindow = user32.NewProc("ShowWindow")
|
||||
getConsoleWnd = kernel32.NewProc("GetConsoleWindow")
|
||||
user32 = syscall.NewLazyDLL("user32.dll")
|
||||
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)
|
||||
}
|
||||
// SW_HIDE = 0:隐藏窗口
|
||||
showWindow.Call(hWnd, 0)
|
||||
}
|
||||
|
||||
// 显示控制台窗口
|
||||
@@ -91,8 +115,40 @@ func (s *sOS) showConsole() {
|
||||
// 获取当前控制台窗口句柄
|
||||
hWnd, _, _ := getConsoleWnd.Call()
|
||||
if hWnd == 0 {
|
||||
// 如果当前进程没有控制台,尝试附加到父进程控制台
|
||||
// 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
|
||||
}
|
||||
// SW_SHOW = 5:显示窗口
|
||||
showWindow.Call(hWnd, 5)
|
||||
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))
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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"`
|
||||
}
|
||||
@@ -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() {}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -1 +1,2 @@
|
||||
package packed
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
5
main.go
5
main.go
@@ -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")
|
||||
|
||||
46
manifest/config/config.yaml
Normal file
46
manifest/config/config.yaml
Normal 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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user