Initialize Clash Web source and deployment documentation
This commit is contained in:
commit
8e0fa00a73
66 files changed
+8532
No files matched your search
@@ -0,0 +1,125 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const coreReleaseEndpoint = "https://api.github.com/repos/MetaCubeX/mihomo/releases/latest"
|
||||
|
||||
var stableVersionPattern = regexp.MustCompile(`^v?(\d+)\.(\d+)\.(\d+)$`)
|
||||
|
||||
type coreRelease struct {
|
||||
Tag string `json:"tag_name"`
|
||||
Published string `json:"published_at"`
|
||||
Draft bool `json:"draft"`
|
||||
Prerelease bool `json:"prerelease"`
|
||||
}
|
||||
|
||||
func stableVersion(version string) ([3]int, bool) {
|
||||
var parts [3]int
|
||||
match := stableVersionPattern.FindStringSubmatch(strings.TrimSpace(version))
|
||||
if len(match) != 4 {
|
||||
return parts, false
|
||||
}
|
||||
for i := range parts {
|
||||
n, err := strconv.Atoi(match[i+1])
|
||||
if err != nil {
|
||||
return parts, false
|
||||
}
|
||||
parts[i] = n
|
||||
}
|
||||
return parts, true
|
||||
}
|
||||
|
||||
func newerStableVersion(current, latest string) *bool {
|
||||
a, currentOK := stableVersion(current)
|
||||
b, latestOK := stableVersion(latest)
|
||||
if !currentOK || !latestOK {
|
||||
return nil
|
||||
}
|
||||
newer := false
|
||||
for i := range a {
|
||||
if b[i] != a[i] {
|
||||
newer = b[i] > a[i]
|
||||
break
|
||||
}
|
||||
}
|
||||
return &newer
|
||||
}
|
||||
|
||||
func fetchCoreRelease(ctx context.Context, client *http.Client) (*coreRelease, error) {
|
||||
ctx, cancel := context.WithTimeout(ctx, 10*time.Second)
|
||||
defer cancel()
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, coreReleaseEndpoint, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.Header.Set("Accept", "application/vnd.github+json")
|
||||
req.Header.Set("User-Agent", "ClashWeb-update-check")
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return nil, errors.New("无法连接 GitHub,请检查网络后重试")
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode == http.StatusForbidden || resp.StatusCode == http.StatusTooManyRequests {
|
||||
return nil, errors.New("GitHub 更新检查暂时受限,请稍后重试")
|
||||
}
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return nil, fmt.Errorf("更新检查失败(HTTP %d)", resp.StatusCode)
|
||||
}
|
||||
data, err := io.ReadAll(io.LimitReader(resp.Body, (2<<20)+1))
|
||||
if err != nil || len(data) > 2<<20 {
|
||||
return nil, errors.New("版本信息读取失败")
|
||||
}
|
||||
var release coreRelease
|
||||
if err := json.Unmarshal(data, &release); err != nil {
|
||||
return nil, errors.New("版本信息格式错误")
|
||||
}
|
||||
if _, ok := stableVersion(release.Tag); !ok || release.Draft || release.Prerelease {
|
||||
return nil, errors.New("未获取到有效的 Mihomo 稳定版本")
|
||||
}
|
||||
return &release, nil
|
||||
}
|
||||
|
||||
func (a *App) latestCoreRelease(ctx context.Context) (*coreRelease, time.Time, error) {
|
||||
a.updatesMu.Lock()
|
||||
defer a.updatesMu.Unlock()
|
||||
if a.updateCache != nil && time.Since(a.updateTime) < 10*time.Minute {
|
||||
return a.updateCache, a.updateTime, nil
|
||||
}
|
||||
release, err := fetchCoreRelease(ctx, a.download)
|
||||
if err != nil {
|
||||
return nil, time.Time{}, err
|
||||
}
|
||||
a.updateCache, a.updateTime = release, time.Now()
|
||||
return release, a.updateTime, nil
|
||||
}
|
||||
|
||||
func (a *App) coreUpdates(w http.ResponseWriter, r *http.Request) {
|
||||
release, checked, err := a.latestCoreRelease(r.Context())
|
||||
if err != nil {
|
||||
writeError(w, http.StatusBadGateway, err.Error())
|
||||
return
|
||||
}
|
||||
var version struct {
|
||||
Version string `json:"version"`
|
||||
}
|
||||
ctx, cancel := context.WithTimeout(r.Context(), 3*time.Second)
|
||||
defer cancel()
|
||||
_ = a.core.Request(ctx, http.MethodGet, "/version", nil, &version)
|
||||
jsonResponse(w, http.StatusOK, map[string]any{
|
||||
"currentVersion": version.Version, "latestVersion": release.Tag,
|
||||
"updateAvailable": newerStableVersion(version.Version, release.Tag),
|
||||
"checkedAt": checked.Format(time.RFC3339), "publishedAt": release.Published,
|
||||
"releaseURL": "https://github.com/MetaCubeX/mihomo/releases/tag/" + release.Tag,
|
||||
})
|
||||
}
|
||||
Reference in new issue
Block a user