更新 update 门户站点界面和后台功能
build-winui / winui (push) Has been cancelled

This commit is contained in:
QWQLwToo
2026-06-27 18:09:11 +08:00
parent 2513eb2903
commit 962a2f2143
56 changed files with 4564 additions and 714 deletions
@@ -139,6 +139,34 @@ func (s *Service) Save(ctx context.Context, version string, req SaveRequest, act
return s.Get(saved.Version)
}
func (s *Service) SyncFromLegacyUpdateInfo(ctx context.Context, raw string, actor string) error {
if strings.TrimSpace(raw) == "" {
return nil
}
item, parsed, formatted, err := parseNotice([]byte(raw), "", "")
if err != nil {
return err
}
item.RawJSON = formatted
current, err := s.store.GetReleaseNotice(item.Version)
if err == nil && current.RawJSON != "" && current.RawJSON != formatted {
_, _ = s.store.SaveReleaseNoticeRevision(item.Version, current.RawJSON, "auto backup before legacy update-info sync", actor)
}
saved, err := s.store.UpsertReleaseNotice(item)
if err != nil {
return err
}
_, _ = s.store.SaveReleaseNoticeRevision(saved.Version, formatted, "synced from update-info.json", actor)
if err := s.writeNoticeFile(saved, formatted); err != nil {
return err
}
if err := s.writeTotalIndex(saved, parsed); err != nil {
return err
}
_ = s.store.InsertAudit(db.AuditLog{Actor: actor, Type: "release_notice.synced", Target: saved.Version, Message: "版本日志已从兼容 update-info.json 同步"})
return nil
}
func (s *Service) Restore(ctx context.Context, version string, revisionID int64, actor string) (Document, error) {
revision, err := s.store.GetReleaseNoticeRevision(version, revisionID)
if err != nil {
@@ -227,10 +255,7 @@ func (s *Service) writeTotalIndex(item db.ReleaseNotice, parsed map[string]any)
func (s *Service) syncLegacyUpdateInfo(item db.ReleaseNotice, parsed map[string]any) error {
path := filepath.Join(s.cfg.UpdatePublicDir, "update-info.json")
payload := map[string]any{}
if data, err := os.ReadFile(path); err == nil {
_ = json.Unmarshal(data, &payload)
}
payload := s.legacyUpdateBase(path)
payload["app_version"] = item.Version
setNonEmpty(payload, "build", item.Build)
setNonEmpty(payload, "channel", item.Channel)
@@ -256,6 +281,36 @@ func (s *Service) syncLegacyUpdateInfo(item db.ReleaseNotice, parsed map[string]
return atomicWrite(path, append(data, '\n'))
}
func (s *Service) legacyUpdateBase(currentPath string) map[string]any {
payload := map[string]any{}
for _, path := range []string{
filepath.Join(s.cfg.LegacyUpdateDir, "public", "update-info.json"),
currentPath,
} {
if data, err := os.ReadFile(path); err == nil {
var doc map[string]any
if json.Unmarshal(data, &doc) == nil {
for key, value := range doc {
payload[key] = value
}
}
}
}
if payload["app_version"] == nil {
if value, ok := payload["appVersion"]; ok {
payload["app_version"] = value
} else if value, ok := payload["latestVersion"]; ok {
payload["app_version"] = value
}
}
if payload["manifest_version"] == nil {
if value, ok := payload["manifestVersion"]; ok {
payload["manifest_version"] = value
}
}
return payload
}
func parseAndFormat(data []byte, fallbackVersion, noticeFile string) (map[string]any, string, error) {
_, parsed, formatted, err := parseNotice(data, fallbackVersion, noticeFile)
return parsed, formatted, err
@@ -270,7 +325,7 @@ func parseNotice(data []byte, fallbackVersion, noticeFile string) (db.ReleaseNot
}
version := firstNonEmpty(stringValue(parsed, "app_version"), stringValue(parsed, "version"), fallbackVersion)
if version == "" {
return db.ReleaseNotice{}, nil, "", errors.New("version or app_version is required")
return db.ReleaseNotice{}, nil, "", errors.New("版本日志需要填写 version app_version")
}
if noticeFile == "" {
noticeFile = version + ".json"
@@ -69,6 +69,56 @@ func TestSaveNoticeSyncsFilesAndLegacyUpdateInfo(t *testing.T) {
}
}
func TestSyncFromLegacyUpdateInfoUpdatesNoticeIndex(t *testing.T) {
root := t.TempDir()
public := filepath.Join(root, "public")
noticeDir := filepath.Join(root, "update-notice")
if err := os.MkdirAll(public, 0o755); err != nil {
t.Fatal(err)
}
if err := os.MkdirAll(noticeDir, 0o755); err != nil {
t.Fatal(err)
}
writeJSON(t, filepath.Join(noticeDir, "total.json"), map[string]any{"schema_version": 1, "versions": []any{}})
cfg := &config.Config{
StorageDir: filepath.Join(root, "storage"),
UpdatePublicDir: public,
UpdateNoticeDir: noticeDir,
Database: config.DatabaseConfig{
Provider: "sqlite",
SQLitePath: filepath.Join(root, "storage", "unified.sqlite"),
FailoverEnabled: true,
HealthIntervalSec: 3600,
MaxOpenConns: 1,
MaxIdleConns: 1,
ConnMaxLifetimeSeconds: 60,
},
}
store, err := db.Open(cfg)
if err != nil {
t.Fatal(err)
}
defer store.Close()
service := NewService(cfg, store)
raw := `{"app_version":"2.0.7.5","title":"YMhut Box 2.0.7.5","message":"随机放映室优化","release_notes":"修复图片源和全屏预览","download_url":"https://update.ymhut.cn/downloads/app.exe"}`
if err := service.SyncFromLegacyUpdateInfo(context.Background(), raw, "admin"); err != nil {
t.Fatal(err)
}
items, err := service.List(10)
if err != nil {
t.Fatal(err)
}
if len(items) != 1 || items[0].Version != "2.0.7.5" || items[0].Title != "YMhut Box 2.0.7.5" {
t.Fatalf("notice list not synced: %#v", items)
}
total := readJSONFile(t, filepath.Join(noticeDir, "total.json"))
if total["latest_version"] != "2.0.7.5" {
t.Fatalf("total index not updated: %#v", total)
}
}
func writeJSON(t *testing.T, path string, payload any) {
t.Helper()
data, err := json.Marshal(payload)