Files
speiseplan/updater.go
clawd df9e7c5541 v0.2.1 — Code Review Fixes
Fixes:
- CRITICAL: compareVersions used string comparison, fails for 1.10 vs 1.9 (now numeric)
- CRITICAL: Double CloseDatabase() in main.go (defer + OnShutdown)
- CRITICAL: Tailwind v4 in package.json but v3 config/syntax (downgraded to v3)
- CRITICAL: react-router-dom v7 with v5 types (switched to v6, removed deprecated types)
- IMPORTANT: UpdatePlanEntry hook signature mismatch (7 args vs Go's 4)
- IMPORTANT: AllergenPicker hidden checkbox inaccessible to screenreaders (sr-only)
- IMPORTANT: weekHelper getWeekFromDate returned wrong ISO year for edge cases
- IMPORTANT: getWeeksInYear bug for years where Dec 31 is in week 1 of next year
- IMPORTANT: getDateFromWeek off-by-one for some years (use Jan 4 anchor)
- IMPORTANT: ProductSearch click-outside missed dropdown (use container ref)
- IMPORTANT: seed.go LastInsertId=0 on INSERT OR IGNORE skip
- IMPORTANT: SQLite missing PRAGMA foreign_keys=ON and WAL mode
- IMPORTANT: AdditivePicker ADDITIVE_NAMES used numeric IDs but data uses letters
- IMPORTANT: Missing role=dialog/aria-modal on all modal dialogs
- IMPORTANT: Missing Escape key handler on ProductForm modal
- IMPORTANT: Sidebar NavLink aria-current used function instead of string
- IMPORTANT: useProducts searchProducts null safety for allergens/additives
- NICE-TO-HAVE: Added aria-live=polite to WeekPlanner for dynamic updates
- NICE-TO-HAVE: Added postcss.config.js for Tailwind v3
- NICE-TO-HAVE: Updated model comments to match actual day/meal conventions
- NICE-TO-HAVE: Modernized vite/typescript/plugin versions
2026-02-20 10:11:54 +00:00

117 lines
2.8 KiB
Go

package main
import (
"encoding/json"
"fmt"
"net/http"
"strconv"
"strings"
"time"
)
const (
// Aktuelle Version der App
CurrentVersion = "1.0.0"
// Update-Check URL
UpdateURL = "https://speiseplan.supertoll.xyz/version.json"
)
// Updater verwaltet App-Updates
type Updater struct {
CurrentVersion string
UpdateURL string
HTTPClient *http.Client
}
// NewUpdater erstellt einen neuen Updater
func NewUpdater() *Updater {
return &Updater{
CurrentVersion: CurrentVersion,
UpdateURL: UpdateURL,
HTTPClient: &http.Client{
Timeout: 10 * time.Second,
},
}
}
// VersionResponse repräsentiert die Antwort vom Update-Server
type VersionResponse struct {
Version string `json:"version"`
DownloadURL string `json:"download_url"`
ReleaseNotes string `json:"release_notes"`
}
// CheckForUpdate prüft ob ein Update verfügbar ist
func (u *Updater) CheckForUpdate() (*UpdateInfo, error) {
resp, err := u.HTTPClient.Get(u.UpdateURL)
if err != nil {
return nil, fmt.Errorf("failed to check for updates: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("update server returned status %d", resp.StatusCode)
}
var versionResp VersionResponse
if err := json.NewDecoder(resp.Body).Decode(&versionResp); err != nil {
return nil, fmt.Errorf("failed to parse version response: %w", err)
}
// Versions-Vergleich (vereinfacht)
available := compareVersions(u.CurrentVersion, versionResp.Version) < 0
return &UpdateInfo{
Available: available,
CurrentVersion: u.CurrentVersion,
LatestVersion: versionResp.Version,
DownloadURL: versionResp.DownloadURL,
ReleaseNotes: versionResp.ReleaseNotes,
}, nil
}
// DownloadUpdate lädt ein verfügbares Update herunter (Stub)
func (u *Updater) DownloadUpdate(downloadURL string) error {
// TODO: Implementierung für das Herunterladen und Installieren von Updates
// Für Phase 1 nur ein Stub
return fmt.Errorf("update download not implemented yet")
}
// compareVersions vergleicht zwei Versionsstrings
// Gibt -1 zurück wenn v1 < v2, 0 wenn v1 == v2, 1 wenn v1 > v2
func compareVersions(v1, v2 string) int {
// Semantic Versioning Vergleich mit numerischem Parsing
v1Parts := strings.Split(strings.TrimPrefix(v1, "v"), ".")
v2Parts := strings.Split(strings.TrimPrefix(v2, "v"), ".")
// Normalisiere auf gleiche Länge
for len(v1Parts) < 3 {
v1Parts = append(v1Parts, "0")
}
for len(v2Parts) < 3 {
v2Parts = append(v2Parts, "0")
}
for i := 0; i < 3; i++ {
n1, err1 := strconv.Atoi(v1Parts[i])
n2, err2 := strconv.Atoi(v2Parts[i])
if err1 != nil || err2 != nil {
// Fallback auf String-Vergleich
if v1Parts[i] < v2Parts[i] {
return -1
}
if v1Parts[i] > v2Parts[i] {
return 1
}
continue
}
if n1 < n2 {
return -1
}
if n1 > n2 {
return 1
}
}
return 0
}