添加 main.go
This commit is contained in:
200
main.go
Normal file
200
main.go
Normal file
@@ -0,0 +1,200 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"crypto/subtle"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"io"
|
||||
"log"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
downloadDir = "./chrome_versions"
|
||||
checkInterval = 24 * time.Hour
|
||||
maxRetries = 3
|
||||
)
|
||||
|
||||
var (
|
||||
authToken string
|
||||
authEnabled bool
|
||||
port string
|
||||
keepVersions int
|
||||
chromeURL string
|
||||
)
|
||||
|
||||
type Version struct {
|
||||
Filename string
|
||||
Size int64
|
||||
Time time.Time
|
||||
}
|
||||
|
||||
func init() {
|
||||
authToken = getEnv("AUTH_TOKEN", "")
|
||||
authEnabled = getEnv("AUTH_ENABLED", "false") == "true" || authToken != ""
|
||||
port = getEnv("PORT", "8080")
|
||||
keepVersions, _ = strconv.Atoi(getEnv("KEEP_VERSIONS", "3"))
|
||||
chromeURL = getEnv("CHROME_URL", "https://www.google.cn/chrome?standalone=1&platform=win64")
|
||||
}
|
||||
|
||||
func getEnv(key, defaultValue string) string {
|
||||
if value := os.Getenv(key); value != "" {
|
||||
return value
|
||||
}
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
func main() {
|
||||
os.MkdirAll(downloadDir, 0755)
|
||||
log.Printf("Auth enabled: %v, Port: %s, Keep versions: %d", authEnabled, port, keepVersions)
|
||||
|
||||
go monitor()
|
||||
|
||||
http.HandleFunc("/", authMiddleware(serveIndex))
|
||||
http.Handle("/download/", authMiddleware(http.StripPrefix("/download/", http.FileServer(http.Dir(downloadDir))).ServeHTTP))
|
||||
|
||||
log.Printf("Server started on :%s", port)
|
||||
log.Fatal(http.ListenAndServe(":"+port, nil))
|
||||
}
|
||||
|
||||
func monitor() {
|
||||
for {
|
||||
delay := time.Duration(rand.Intn(3600)) * time.Second
|
||||
time.Sleep(delay)
|
||||
|
||||
checkAndDownload()
|
||||
time.Sleep(checkInterval)
|
||||
}
|
||||
}
|
||||
|
||||
func checkAndDownload() {
|
||||
var data []byte
|
||||
var err error
|
||||
|
||||
for i := 0; i < maxRetries; i++ {
|
||||
resp, err := http.Get(chromeURL)
|
||||
if err != nil {
|
||||
log.Printf("Attempt %d failed: %v", i+1, err)
|
||||
time.Sleep(time.Duration(i+1) * 10 * time.Second)
|
||||
continue
|
||||
}
|
||||
|
||||
data, err = io.ReadAll(resp.Body)
|
||||
resp.Body.Close()
|
||||
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
log.Printf("Attempt %d read failed: %v", i+1, err)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
log.Println("All retries failed")
|
||||
return
|
||||
}
|
||||
|
||||
hash := fmt.Sprintf("%x", md5.Sum(data))
|
||||
filename := fmt.Sprintf("chrome_%s_%s.exe", time.Now().Format("20060102"), hash[:8])
|
||||
filepath := filepath.Join(downloadDir, filename)
|
||||
|
||||
if _, err := os.Stat(filepath); err == nil {
|
||||
log.Println("File already exists, skipping")
|
||||
return
|
||||
}
|
||||
|
||||
if err := os.WriteFile(filepath, data, 0644); err != nil {
|
||||
log.Printf("Error saving file: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
log.Printf("Downloaded: %s", filename)
|
||||
cleanupOldVersions()
|
||||
}
|
||||
|
||||
func cleanupOldVersions() {
|
||||
files, _ := os.ReadDir(downloadDir)
|
||||
if len(files) <= keepVersions {
|
||||
return
|
||||
}
|
||||
|
||||
var versions []Version
|
||||
for _, f := range files {
|
||||
if strings.HasSuffix(f.Name(), ".exe") {
|
||||
info, _ := f.Info()
|
||||
versions = append(versions, Version{
|
||||
Filename: f.Name(),
|
||||
Time: info.ModTime(),
|
||||
Size: info.Size(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
sort.Slice(versions, func(i, j int) bool {
|
||||
return versions[i].Time.After(versions[j].Time)
|
||||
})
|
||||
|
||||
for i := keepVersions; i < len(versions); i++ {
|
||||
os.Remove(filepath.Join(downloadDir, versions[i].Filename))
|
||||
}
|
||||
}
|
||||
|
||||
func authMiddleware(next http.HandlerFunc) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
if authEnabled {
|
||||
token := r.URL.Query().Get("token")
|
||||
if subtle.ConstantTimeCompare([]byte(token), []byte(authToken)) != 1 {
|
||||
http.Error(w, "Unauthorized", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
}
|
||||
next(w, r)
|
||||
}
|
||||
}
|
||||
|
||||
func serveIndex(w http.ResponseWriter, r *http.Request) {
|
||||
files, _ := os.ReadDir(downloadDir)
|
||||
var versions []Version
|
||||
|
||||
for _, f := range files {
|
||||
if strings.HasSuffix(f.Name(), ".exe") {
|
||||
info, _ := f.Info()
|
||||
versions = append(versions, Version{
|
||||
Filename: f.Name(),
|
||||
Time: info.ModTime(),
|
||||
Size: info.Size(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
sort.Slice(versions, func(i, j int) bool {
|
||||
return versions[i].Time.After(versions[j].Time)
|
||||
})
|
||||
|
||||
token := r.URL.Query().Get("token")
|
||||
tmpl := template.Must(template.New("index").Parse(`
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head><title>Chrome Offline Installer</title></head>
|
||||
<body>
|
||||
<h1>Chrome Offline Versions</h1>
|
||||
<ul>
|
||||
{{range .Versions}}
|
||||
<li><a href="/download/{{.Filename}}{{if $.Token}}?token={{$.Token}}{{end}}">{{.Filename}}</a> ({{.Size}} bytes, {{.Time.Format "2006-01-02 15:04"}})</li>
|
||||
{{end}}
|
||||
</ul>
|
||||
</body>
|
||||
</html>
|
||||
`))
|
||||
tmpl.Execute(w, struct {
|
||||
Versions []Version
|
||||
Token string
|
||||
}{versions, token})
|
||||
}
|
||||
Reference in New Issue
Block a user