更新 main.go
This commit is contained in:
116
main.go
116
main.go
@@ -3,6 +3,7 @@ package main
|
|||||||
import (
|
import (
|
||||||
"crypto/md5"
|
"crypto/md5"
|
||||||
"crypto/subtle"
|
"crypto/subtle"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"html/template"
|
"html/template"
|
||||||
"io"
|
"io"
|
||||||
@@ -21,6 +22,7 @@ const (
|
|||||||
downloadDir = "./chrome_versions"
|
downloadDir = "./chrome_versions"
|
||||||
checkInterval = 24 * time.Hour
|
checkInterval = 24 * time.Hour
|
||||||
maxRetries = 3
|
maxRetries = 3
|
||||||
|
versionAPI = "https://versionhistory.googleapis.com/v1/chrome/platforms/win64/channels/stable/versions"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -28,7 +30,6 @@ var (
|
|||||||
authEnabled bool
|
authEnabled bool
|
||||||
port string
|
port string
|
||||||
keepVersions int
|
keepVersions int
|
||||||
chromeURL string
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Version struct {
|
type Version struct {
|
||||||
@@ -37,12 +38,20 @@ type Version struct {
|
|||||||
Time time.Time
|
Time time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ChromeVersion struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Version string `json:"version"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ChromeVersionResponse struct {
|
||||||
|
Versions []ChromeVersion `json:"versions"`
|
||||||
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
authToken = getEnv("AUTH_TOKEN", "")
|
authToken = getEnv("AUTH_TOKEN", "")
|
||||||
authEnabled = getEnv("AUTH_ENABLED", "false") == "true" || authToken != ""
|
authEnabled = getEnv("AUTH_ENABLED", "false") == "true" || authToken != ""
|
||||||
port = getEnv("PORT", "8080")
|
port = getEnv("PORT", "8080")
|
||||||
keepVersions, _ = strconv.Atoi(getEnv("KEEP_VERSIONS", "3"))
|
keepVersions, _ = strconv.Atoi(getEnv("KEEP_VERSIONS", "3"))
|
||||||
chromeURL = getEnv("CHROME_URL", "https://dl.google.com/tag/s/appguid%3D%7B8A69D345-D564-463C-AFF1-A69D9E530F96%7D%26iid%3D%7B00000000-0000-0000-0000-000000000000%7D%26lang%3Dzh-CN%26browser%3D4%26usagestats%3D0%26appname%3DGoogle%2520Chrome%26needsadmin%3Dprefers%26ap%3Dx64-stable-statsdef_1%26installdataindex%3Dempty/chrome/install/ChromeStandaloneSetup64.exe")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func getEnv(key, defaultValue string) string {
|
func getEnv(key, defaultValue string) string {
|
||||||
@@ -56,7 +65,6 @@ func main() {
|
|||||||
os.MkdirAll(downloadDir, 0755)
|
os.MkdirAll(downloadDir, 0755)
|
||||||
log.Printf("Auth enabled: %v, Port: %s, Keep versions: %d", authEnabled, port, keepVersions)
|
log.Printf("Auth enabled: %v, Port: %s, Keep versions: %d", authEnabled, port, keepVersions)
|
||||||
|
|
||||||
// 启动时立即检查一次
|
|
||||||
log.Println("Performing initial check...")
|
log.Println("Performing initial check...")
|
||||||
checkAndDownload()
|
checkAndDownload()
|
||||||
|
|
||||||
@@ -79,34 +87,86 @@ func monitor() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkAndDownload() {
|
func getLatestVersion() (string, error) {
|
||||||
var data []byte
|
resp, err := http.Get(versionAPI)
|
||||||
var err error
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
var versionResp ChromeVersionResponse
|
||||||
|
if err := json.NewDecoder(resp.Body).Decode(&versionResp); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(versionResp.Versions) == 0 {
|
||||||
|
return "", fmt.Errorf("no versions found")
|
||||||
|
}
|
||||||
|
|
||||||
|
return versionResp.Versions[0].Version, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkAndDownload() {
|
||||||
|
version, err := getLatestVersion()
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Failed to get latest version: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("Latest Chrome version: %s", version)
|
||||||
|
|
||||||
|
// 尝试多个可能的下载 URL 模式
|
||||||
|
urls := []string{
|
||||||
|
fmt.Sprintf("https://dl.google.com/release2/chrome/%%s_%s/%s_chrome_installer.exe", version, version),
|
||||||
|
fmt.Sprintf("https://dl.google.com/tag/s/appguid%%3D%%7B8A69D345-D564-463C-AFF1-A69D9E530F96%%7D%%26iid%%3D%%7B00000000-0000-0000-0000-000000000000%%7D%%26lang%%3Dzh-CN%%26browser%%3D4%%26usagestats%%3D0%%26appname%%3DGoogle%%2520Chrome%%26needsadmin%%3Dprefers%%26ap%%3Dx64-stable-statsdef_1%%26installdataindex%%3Dempty/chrome/install/ChromeStandaloneSetup64.exe"),
|
||||||
|
}
|
||||||
|
|
||||||
|
var data []byte
|
||||||
|
client := &http.Client{
|
||||||
|
CheckRedirect: func(req *http.Request, via []*http.Request) error {
|
||||||
|
log.Printf("Redirecting to: %s", req.URL.String())
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, urlPattern := range urls {
|
||||||
for i := 0; i < maxRetries; i++ {
|
for i := 0; i < maxRetries; i++ {
|
||||||
resp, err := http.Get(chromeURL)
|
url := urlPattern
|
||||||
|
if strings.Contains(url, "%s") {
|
||||||
|
// 跳过需要 hash 的 URL
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("Trying URL: %s", url)
|
||||||
|
resp, err := client.Get(url)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Attempt %d failed: %v", i+1, err)
|
log.Printf("Attempt %d failed: %v", i+1, err)
|
||||||
time.Sleep(time.Duration(i+1) * 10 * time.Second)
|
time.Sleep(time.Duration(i+1) * 10 * time.Second)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
contentType := resp.Header.Get("Content-Type")
|
||||||
|
contentLength := resp.Header.Get("Content-Length")
|
||||||
|
log.Printf("Content-Type: %s, Content-Length: %s", contentType, contentLength)
|
||||||
|
|
||||||
data, err = io.ReadAll(resp.Body)
|
data, err = io.ReadAll(resp.Body)
|
||||||
resp.Body.Close()
|
resp.Body.Close()
|
||||||
|
|
||||||
if err == nil {
|
if err == nil && len(data) > 10000000 && !strings.Contains(contentType, "text/html") {
|
||||||
break
|
goto success
|
||||||
}
|
|
||||||
log.Printf("Attempt %d read failed: %v", i+1, err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
log.Printf("Invalid response (size: %d bytes)", len(data))
|
||||||
log.Println("All retries failed")
|
time.Sleep(time.Duration(i+1) * 10 * time.Second)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Println("All download attempts failed")
|
||||||
return
|
return
|
||||||
}
|
|
||||||
|
|
||||||
|
success:
|
||||||
hash := fmt.Sprintf("%x", md5.Sum(data))
|
hash := fmt.Sprintf("%x", md5.Sum(data))
|
||||||
filename := fmt.Sprintf("chrome_%s_%s.exe", time.Now().Format("20060102"), hash[:8])
|
filename := fmt.Sprintf("chrome_%s_%s.exe", version, hash[:8])
|
||||||
filepath := filepath.Join(downloadDir, filename)
|
filepath := filepath.Join(downloadDir, filename)
|
||||||
|
|
||||||
if _, err := os.Stat(filepath); err == nil {
|
if _, err := os.Stat(filepath); err == nil {
|
||||||
@@ -192,14 +252,30 @@ func serveIndex(w http.ResponseWriter, r *http.Request) {
|
|||||||
<h1>Chrome Offline Versions</h1>
|
<h1>Chrome Offline Versions</h1>
|
||||||
<ul>
|
<ul>
|
||||||
{{range .Versions}}
|
{{range .Versions}}
|
||||||
<li><a href="/download/{{.Filename}}{{if $.Token}}?token={{$.Token}}{{end}}">{{.Filename}}</a> ({{.Size}} bytes, {{.Time.Format "2006-01-02 15:04"}})</li>
|
<li><a href="/download/{{.Filename}}{{if $.Token}}?token={{$.Token}}{{end}}">{{.Filename}}</a> ({{printf "%.2f" .SizeMB}} MB, {{.Time.Format "2006-01-02 15:04"}})</li>
|
||||||
{{end}}
|
{{end}}
|
||||||
</ul>
|
</ul>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
`))
|
`))
|
||||||
tmpl.Execute(w, struct {
|
|
||||||
Versions []Version
|
type VersionDisplay struct {
|
||||||
Token string
|
Filename string
|
||||||
}{versions, token})
|
SizeMB float64
|
||||||
|
Time time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
var displayVersions []VersionDisplay
|
||||||
|
for _, v := range versions {
|
||||||
|
displayVersions = append(displayVersions, VersionDisplay{
|
||||||
|
Filename: v.Filename,
|
||||||
|
SizeMB: float64(v.Size) / 1024 / 1024,
|
||||||
|
Time: v.Time,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
tmpl.Execute(w, struct {
|
||||||
|
Versions []VersionDisplay
|
||||||
|
Token string
|
||||||
|
}{displayVersions, token})
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user