diff --git a/configs/config.yaml b/configs/config.yaml index 2ac1f65..f971d86 100644 --- a/configs/config.yaml +++ b/configs/config.yaml @@ -105,3 +105,25 @@ rate_limiting: auth: login_attempts_per_minute: 10 # Login attempts per IP per minute burst_size: 5 # Login burst allowance per IP + +# Site branding configuration +branding: + site_name: "Sovbit Gateway" # Site name shown in headers/titles + logo_url: "https://files.sovbit.host/media/44dc1c2db9c3fbd7bee9257eceb52be3cf8c40baf7b63f46e56b58a131c74f0b/7c96148e8244bfdb0294f44824267b6b08f2dcc29db8c6571d88ab8e22a1abf1.webp" # URL to your logo image (optional) + logo_width: "auto" # Logo width (e.g., "150px", "auto") + logo_height: "32px" # Logo height (e.g., "32px", "auto") + favicon_url: "" # URL to your favicon (optional) + description: "Decentralized file sharing gateway" # Site description + footer_text: "" # Footer text (optional) + support_url: "" # Support/contact URL (optional) + +# Example sovbit.host configuration: +# branding: +# site_name: "sovbit.host" +# logo_url: "https://sovbit.host/logo.png" +# logo_width: "120px" +# logo_height: "30px" +# favicon_url: "https://sovbit.host/favicon.ico" +# description: "Sovereign Bitcoin hosting services" +# footer_text: "Powered by sovbit.host" +# support_url: "https://sovbit.host/support" diff --git a/internal/admin/handlers.go b/internal/admin/handlers.go index 2668eb3..ec30c69 100644 --- a/internal/admin/handlers.go +++ b/internal/admin/handlers.go @@ -210,18 +210,7 @@ func (ah *AdminHandlers) AdminUsersHandler(w http.ResponseWriter, r *http.Reques users = append(users, user) } - // Fetch profile metadata for all users - pubkeys := make([]string, len(users)) - for i, user := range users { - pubkeys[i] = user.Pubkey - } - - profiles := ah.profileFetcher.GetBatchProfiles(pubkeys) - for i := range users { - if profile, exists := profiles[users[i].Pubkey]; exists { - users[i].Profile = profile - } - } + // Skip profile fetching - let frontend handle it asynchronously // Log admin action ah.adminAuth.LogAdminAction(adminPubkey, "view_users", "", "Admin viewed user list") @@ -305,24 +294,7 @@ func (ah *AdminHandlers) AdminFilesHandler(w http.ResponseWriter, r *http.Reques files = append(files, file) } - // Fetch profile metadata for file owners - ownerPubkeys := make([]string, 0) - for _, file := range files { - if file.OwnerPubkey != "" { - ownerPubkeys = append(ownerPubkeys, file.OwnerPubkey) - } - } - - if len(ownerPubkeys) > 0 { - profiles := ah.profileFetcher.GetBatchProfiles(ownerPubkeys) - for i := range files { - if files[i].OwnerPubkey != "" { - if profile, exists := profiles[files[i].OwnerPubkey]; exists { - files[i].OwnerProfile = profile - } - } - } - } + // Skip profile fetching - let frontend handle it asynchronously // Log admin action ah.adminAuth.LogAdminAction(adminPubkey, "view_files", "", "Admin viewed file list") diff --git a/internal/api/handlers.go b/internal/api/handlers.go index 07d64f1..2572243 100644 --- a/internal/api/handlers.go +++ b/internal/api/handlers.go @@ -2729,6 +2729,9 @@ func RegisterRoutes(r *mux.Router, cfg *config.Config, storage *storage.Backend) // System stats endpoint (public) r.HandleFunc("/stats", systemStatsHandler(storage, trackerInstance)).Methods("GET") + // Branding configuration endpoint (public) + r.HandleFunc("/branding", brandingHandler(cfg)).Methods("GET") + // DHT stats endpoint (public) r.HandleFunc("/dht/stats", gateway.DHTStatsHandler).Methods("GET") @@ -3129,6 +3132,27 @@ func healthHandler(w http.ResponseWriter, r *http.Request) { json.NewEncoder(w).Encode(map[string]string{"status": "ok"}) } +func brandingHandler(cfg *config.Config) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + w.Header().Set("Access-Control-Allow-Origin", "*") + + // Return branding configuration + branding := map[string]string{ + "site_name": cfg.Branding.SiteName, + "logo_url": cfg.Branding.LogoURL, + "logo_width": cfg.Branding.LogoWidth, + "logo_height": cfg.Branding.LogoHeight, + "favicon_url": cfg.Branding.FaviconURL, + "description": cfg.Branding.Description, + "footer_text": cfg.Branding.FooterText, + "support_url": cfg.Branding.SupportURL, + } + + json.NewEncoder(w).Encode(branding) + } +} + func systemStatsHandler(storage *storage.Backend, trackerInstance *tracker.Tracker) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") diff --git a/internal/config/config.go b/internal/config/config.go index e3b4162..604e848 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -23,6 +23,7 @@ type Config struct { Proxy ProxyConfig `yaml:"proxy"` Admin AdminConfig `yaml:"admin"` RateLimiting RateLimitingConfig `yaml:"rate_limiting"` + Branding BrandingConfig `yaml:"branding"` } // GatewayConfig configures the HTTP API gateway @@ -155,6 +156,18 @@ type AuthRateConfig struct { BurstSize int `yaml:"burst_size"` } +// BrandingConfig configures site branding and appearance +type BrandingConfig struct { + SiteName string `yaml:"site_name"` + LogoURL string `yaml:"logo_url"` + LogoWidth string `yaml:"logo_width"` + LogoHeight string `yaml:"logo_height"` + FaviconURL string `yaml:"favicon_url"` + Description string `yaml:"description"` + FooterText string `yaml:"footer_text"` + SupportURL string `yaml:"support_url"` +} + // LoadConfig loads configuration from a YAML file func LoadConfig(filename string) (*Config, error) { data, err := os.ReadFile(filename) @@ -172,6 +185,14 @@ func LoadConfig(filename string) (*Config, error) { config.Mode = "unified" } + // Set branding defaults + if config.Branding.SiteName == "" { + config.Branding.SiteName = "BitTorrent Gateway" + } + if config.Branding.Description == "" { + config.Branding.Description = "Decentralized file sharing gateway" + } + return &config, nil } diff --git a/internal/web/admin.html b/internal/web/admin.html index 6bc24f4..3db6513 100644 --- a/internal/web/admin.html +++ b/internal/web/admin.html @@ -64,6 +64,26 @@ padding: 12px; text-align: left; border-bottom: 1px solid var(--border-color); + word-wrap: break-word; + word-break: break-word; + max-width: 200px; + } + + .admin-table th:first-child, + .admin-table td:first-child { + max-width: 50px; + } + + .admin-table .hash-short { + max-width: 120px; + font-family: monospace; + font-size: 0.9rem; + } + + .admin-table .owner-cell, + .admin-table .user-profile-cell { + max-width: 180px; + overflow: visible; } .admin-table th { @@ -143,16 +163,12 @@ color: black; } - .hash-short { - font-family: monospace; - font-size: 0.9rem; - }