feat(controller): Hub asset syncer for logos and screenshots
Add internal/assets package that downloads and caches app assets from Hub API with SHA-256 change detection. Assets resolve from synced cache first, falling back to baked-in directory. Daily sync schedule + on-demand POST /api/assets/sync endpoint. Config: assets.sync_enabled + assets.sync_schedule (default 05:00) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -13,6 +13,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"gitea.dooplex.hu/admin/felhom-controller/internal/assets"
|
||||
"gitea.dooplex.hu/admin/felhom-controller/internal/backup"
|
||||
"gitea.dooplex.hu/admin/felhom-controller/internal/config"
|
||||
"gitea.dooplex.hu/admin/felhom-controller/internal/metrics"
|
||||
@@ -41,6 +42,14 @@ type Router struct {
|
||||
|
||||
// OnConfigApplied is called after a successful config apply (e.g., to push infra backup).
|
||||
OnConfigApplied func()
|
||||
|
||||
// Asset syncer for on-demand Hub asset sync
|
||||
assetsSyncer *assets.Syncer
|
||||
}
|
||||
|
||||
// SetAssetsSyncer sets the Hub asset syncer for on-demand sync triggers.
|
||||
func (r *Router) SetAssetsSyncer(as *assets.Syncer) {
|
||||
r.assetsSyncer = as
|
||||
}
|
||||
|
||||
func NewRouter(cfg *config.Config, configPath string, sett *settings.Settings, stackMgr *stacks.Manager, syncer *catalogsync.Syncer, cpuCollector *system.CPUCollector, backupMgr *backup.Manager, crossDrive *backup.CrossDriveRunner, metricsStore *metrics.MetricsStore, updater *selfupdate.Updater, notif *notify.Notifier, logger *log.Logger) *Router {
|
||||
@@ -197,6 +206,14 @@ func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
case path == "/metrics/sysinfo" && req.Method == http.MethodGet:
|
||||
r.metricsSysInfo(w, req)
|
||||
|
||||
// POST /api/assets/sync — trigger immediate asset sync from Hub
|
||||
case path == "/assets/sync" && req.Method == http.MethodPost:
|
||||
r.triggerAssetSync(w, req)
|
||||
|
||||
// GET /api/assets/status — get asset sync status
|
||||
case path == "/assets/status" && req.Method == http.MethodGet:
|
||||
r.assetSyncStatus(w, req)
|
||||
|
||||
default:
|
||||
writeJSON(w, http.StatusNotFound, apiResponse{OK: false, Error: "endpoint not found"})
|
||||
}
|
||||
@@ -1005,6 +1022,30 @@ func (r *Router) configContent(w http.ResponseWriter, _ *http.Request) {
|
||||
w.Write(data)
|
||||
}
|
||||
|
||||
// --- Asset sync handlers ---
|
||||
|
||||
func (r *Router) triggerAssetSync(w http.ResponseWriter, req *http.Request) {
|
||||
if r.assetsSyncer == nil {
|
||||
writeJSON(w, http.StatusOK, apiResponse{OK: false, Error: "asset sync not configured"})
|
||||
return
|
||||
}
|
||||
r.logger.Println("[API] Manual asset sync requested")
|
||||
go func() {
|
||||
if err := r.assetsSyncer.Sync(context.Background()); err != nil {
|
||||
r.logger.Printf("[WARN] Manual asset sync failed: %v", err)
|
||||
}
|
||||
}()
|
||||
writeJSON(w, http.StatusOK, apiResponse{OK: true, Message: "Asset sync started"})
|
||||
}
|
||||
|
||||
func (r *Router) assetSyncStatus(w http.ResponseWriter, _ *http.Request) {
|
||||
if r.assetsSyncer == nil {
|
||||
writeJSON(w, http.StatusOK, apiResponse{OK: true, Data: map[string]string{"status": "not_configured"}})
|
||||
return
|
||||
}
|
||||
writeJSON(w, http.StatusOK, apiResponse{OK: true, Data: r.assetsSyncer.Status()})
|
||||
}
|
||||
|
||||
func writeJSON(w http.ResponseWriter, status int, v interface{}) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(status)
|
||||
|
||||
Reference in New Issue
Block a user