feat(hub): asset management API with PVC storage and image seed
Add internal/assets package that manages app assets (logos, screenshots)
on Hub PVC with automatic seeding from baked-in image copy on first run.
Two new API endpoints: GET /assets/manifest (JSON with SHA-256 checksums)
and GET /assets/file/{name} for controllers to sync assets.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -11,6 +11,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"gitea.dooplex.hu/admin/felhom-hub/internal/assets"
|
||||
"gitea.dooplex.hu/admin/felhom-hub/internal/configgen"
|
||||
"gitea.dooplex.hu/admin/felhom-hub/internal/notify"
|
||||
"gitea.dooplex.hu/admin/felhom-hub/internal/store"
|
||||
@@ -31,6 +32,7 @@ type Handler struct {
|
||||
httpClient *http.Client
|
||||
templateProvider ConfigTemplateProvider
|
||||
dispatcher *notify.Dispatcher
|
||||
assetsMgr *assets.Manager
|
||||
}
|
||||
|
||||
// New creates a new API handler.
|
||||
@@ -51,6 +53,11 @@ func (h *Handler) SetDispatcher(d *notify.Dispatcher) {
|
||||
h.dispatcher = d
|
||||
}
|
||||
|
||||
// SetAssetManager sets the asset manager for serving app assets to controllers.
|
||||
func (h *Handler) SetAssetManager(am *assets.Manager) {
|
||||
h.assetsMgr = am
|
||||
}
|
||||
|
||||
// checkAuth verifies the Bearer token against the global API key or a per-customer API key.
|
||||
// Returns true if authorized.
|
||||
func (h *Handler) checkAuth(r *http.Request) bool {
|
||||
@@ -115,6 +122,11 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
case r.Method == http.MethodGet && strings.HasPrefix(path, "/config/"):
|
||||
customerID := strings.TrimPrefix(path, "/config/")
|
||||
h.handleConfigRetrieve(w, r, customerID)
|
||||
case r.Method == http.MethodGet && path == "/assets/manifest":
|
||||
h.handleAssetsManifest(w, r)
|
||||
case r.Method == http.MethodGet && strings.HasPrefix(path, "/assets/file/"):
|
||||
filename := strings.TrimPrefix(path, "/assets/file/")
|
||||
h.handleAssetFile(w, r, filename)
|
||||
default:
|
||||
http.NotFound(w, r)
|
||||
}
|
||||
@@ -755,3 +767,40 @@ Felhom.eu monitoring`
|
||||
|
||||
return subject, emailText
|
||||
}
|
||||
|
||||
// --- Asset endpoints ---
|
||||
|
||||
func (h *Handler) handleAssetsManifest(w http.ResponseWriter, r *http.Request) {
|
||||
if !h.checkAuth(r) {
|
||||
http.Error(w, "Unauthorized", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
if h.assetsMgr == nil {
|
||||
http.Error(w, "Assets not configured", http.StatusServiceUnavailable)
|
||||
return
|
||||
}
|
||||
|
||||
data, err := h.assetsMgr.MarshalManifestJSON()
|
||||
if err != nil {
|
||||
http.Error(w, "Internal error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.Write(data)
|
||||
}
|
||||
|
||||
func (h *Handler) handleAssetFile(w http.ResponseWriter, r *http.Request, filename string) {
|
||||
if !h.checkAuth(r) {
|
||||
http.Error(w, "Unauthorized", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
if h.assetsMgr == nil {
|
||||
http.Error(w, "Assets not configured", http.StatusServiceUnavailable)
|
||||
return
|
||||
}
|
||||
|
||||
h.assetsMgr.ServeFile(w, r, filename)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user