Files
Embalming_Girl_Server/admin/update.go
T
2026-06-20 18:18:50 +08:00

161 lines
4.3 KiB
Go

package admin
import (
"crypto/sha256"
"encoding/hex"
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"os"
"path/filepath"
"time"
)
type UpdateInfo struct {
Version string `json:"version"`
Platform string `json:"platform"`
FileName string `json:"filename"`
SHA256 string `json:"sha256"`
Size int64 `json:"size"`
ReleaseNotes string `json:"release_notes"`
Mandatory bool `json:"mandatory"`
PublishedAt string `json:"published_at"`
}
var updatesDir string
func initUpdatesDir() {
exe, _ := os.Executable()
updatesDir = filepath.Join(filepath.Dir(exe), "data", "updates")
os.MkdirAll(updatesDir, 0755)
}
func RegisterUpdateRoutes(mux *http.ServeMux) {
initUpdatesDir()
mux.HandleFunc("/api/update/check", handleUpdateCheck)
mux.Handle("/updates/", http.StripPrefix("/updates/",
http.FileServer(http.Dir(updatesDir))))
mux.HandleFunc("/api/admin/publish_update", authWrap(handlePublishUpdate))
mux.HandleFunc("/api/admin/update_info", handleGetUpdateInfo)
mux.HandleFunc("/api/admin/delete_update", authWrap(handleDeleteUpdate))
}
func getUpdateInfoPath() string {
return filepath.Join(updatesDir, "latest.json")
}
func loadUpdateInfo() *UpdateInfo {
data, err := os.ReadFile(getUpdateInfoPath())
if err != nil {
return nil
}
var info UpdateInfo
if json.Unmarshal(data, &info) != nil {
return nil
}
return &info
}
func saveUpdateInfo(info *UpdateInfo) error {
data, _ := json.MarshalIndent(info, "", " ")
return os.WriteFile(getUpdateInfoPath(), data, 0644)
}
func handleUpdateCheck(w http.ResponseWriter, r *http.Request) {
clientVersion := r.URL.Query().Get("version")
info := loadUpdateInfo()
w.Header().Set("Content-Type", "application/json")
if info == nil || info.Version == clientVersion {
json.NewEncoder(w).Encode(map[string]interface{}{
"update_available": false,
"current_version": clientVersion,
})
return
}
json.NewEncoder(w).Encode(map[string]interface{}{
"update_available": true,
"latest_version": info.Version,
"download_url": "/updates/" + info.FileName,
"sha256": info.SHA256,
"size": info.Size,
"release_notes": info.ReleaseNotes,
"mandatory": info.Mandatory,
})
}
func handleGetUpdateInfo(w http.ResponseWriter, r *http.Request) {
info := loadUpdateInfo()
w.Header().Set("Content-Type", "application/json")
if info == nil {
json.NewEncoder(w).Encode(map[string]interface{}{"published": false})
return
}
json.NewEncoder(w).Encode(map[string]interface{}{
"published": true, "info": info,
})
}
func handleDeleteUpdate(w http.ResponseWriter, r *http.Request) {
info := loadUpdateInfo()
if info != nil {
os.Remove(filepath.Join(updatesDir, info.FileName))
os.Remove(getUpdateInfoPath())
}
json.NewEncoder(w).Encode(map[string]interface{}{"ok": true})
}
func handlePublishUpdate(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "POST only", 405)
return
}
r.ParseMultipartForm(200 << 20)
version := r.FormValue("version")
notes := r.FormValue("release_notes")
mandatory := r.FormValue("mandatory") == "true"
file, header, err := r.FormFile("file")
if err != nil {
json.NewEncoder(w).Encode(map[string]interface{}{"ok": false, "message": "文件上传失败"})
return
}
defer file.Close()
dstPath := filepath.Join(updatesDir, header.Filename)
dst, err := os.Create(dstPath)
if err != nil {
json.NewEncoder(w).Encode(map[string]interface{}{"ok": false, "message": "无法创建文件"})
return
}
hasher := sha256.New()
size, _ := io.Copy(io.MultiWriter(dst, hasher), file)
dst.Close()
hash := hex.EncodeToString(hasher.Sum(nil))
info := &UpdateInfo{
Version: version, Platform: "win64", FileName: header.Filename,
SHA256: hash, Size: size, ReleaseNotes: notes, Mandatory: mandatory,
PublishedAt: time.Now().Format("2006-01-02 15:04:05"),
}
saveUpdateInfo(info)
log.Printf("Update published: v%s (%s, %d bytes)", version, header.Filename, size)
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]interface{}{"ok": true, "info": info})
}
func FormatSize(b int64) string {
if b < 1024 {
return fmt.Sprintf("%d B", b)
} else if b < 1024*1024 {
return fmt.Sprintf("%.1f KB", float64(b)/1024)
}
return fmt.Sprintf("%.1f MB", float64(b)/1024/1024)
}