From 844c85b49825f2d667e40dbbce39b037299e00f3 Mon Sep 17 00:00:00 2001 From: Tomas Dvorak Date: Fri, 30 May 2025 09:21:52 +0200 Subject: [PATCH] fef --- admin-dashboard.html | 79 ++++++++++++--------- main.go | 161 ++++++++++++++++++++++++++++++------------- 2 files changed, 160 insertions(+), 80 deletions(-) diff --git a/admin-dashboard.html b/admin-dashboard.html index f1e7bc5..e150a45 100644 --- a/admin-dashboard.html +++ b/admin-dashboard.html @@ -1459,15 +1459,36 @@ function loadHardcodedApps() { // Load dynamic apps async function loadDynamicApps() { - const dynamicAppsList = document.getElementById('dynamicAppsList'); + console.log("Loading dynamic apps..."); + const dynamicAppsContainer = document.getElementById('dynamicApps'); try { - const response = await fetch('/api/apps'); - if (!response.ok) throw new Error('Nepodařilo se načíst seznam aplikací'); + const token = localStorage.getItem('token'); + if (!token) { + window.location.href = '/login.html'; + return; + } + + const response = await fetch('/api/apps', { + headers: { + 'Authorization': `Bearer ${token}`, + 'Content-Type': 'application/json' + } + }); + + if (!response.ok) { + if (response.status === 401) { + // Token expired or invalid, redirect to login + window.location.href = '/login.html'; + return; + } + throw new Error(`HTTP error! status: ${response.status}`); + } const apps = await response.json(); + console.log("Loaded dynamic apps:", apps); - if (apps.length === 0) { + if (!Array.isArray(apps) || apps.length === 0) { dynamicAppsList.innerHTML = `
@@ -1477,7 +1498,8 @@ async function loadDynamicApps() { return; } - dynamicAppsList.innerHTML = apps + // Filter out hardcoded apps and map to HTML + const dynamicApps = apps .filter(app => !app.id || !app.id.startsWith('hardcoded-')) .map(app => `
@@ -1489,43 +1511,34 @@ async function loadDynamicApps() {
` }
-

${app.name}

-

${app.url}

- ${app.description ? `

${app.description}

` : ''} +

${app.name || 'Neznámá aplikace'}

+

${app.url || ''}

- -
`).join(''); - - // Add event listeners to buttons - document.querySelectorAll('.edit-app-btn').forEach(btn => { - btn.addEventListener('click', (e) => { - e.stopPropagation(); - const appId = btn.dataset.appId; - editApp(appId); - }); - }); - - document.querySelectorAll('.delete-app-btn').forEach(btn => { - btn.addEventListener('click', (e) => { - e.stopPropagation(); - const appId = btn.dataset.appId; - if (confirm('Opravdu chcete tuto aplikaci smazat?')) { - deleteApp(appId); - } - }); - }); - + + if (dynamicApps.length > 0) { + dynamicAppsList.innerHTML = dynamicApps; + } else { + dynamicAppsList.innerHTML = ` +
+ +

Žádné vlastní aplikace nebyly nalezeny

+
+ `; + } + } catch (error) { - console.error('Chyba při načítání vlastních aplikací:', error); + console.error('Error loading dynamic apps:', error); dynamicAppsList.innerHTML = `
@@ -1533,7 +1546,9 @@ async function loadDynamicApps() {
-

Chyba při načítání vlastních aplikací: ${error.message}

+

+ Chyba při načítání aplikací: ${error.message} +

diff --git a/main.go b/main.go index 84cafe0..4d0087d 100644 --- a/main.go +++ b/main.go @@ -65,6 +65,60 @@ func main() { kontaktURL, _ := url.Parse("http://webportal:8080") kontaktProxy := httputil.NewSingleHostReverseProxy(kontaktURL) + // CORS middleware + corsMiddleware := func(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + // Allow all origins for development + origin := r.Header.Get("Origin") + if origin == "" { + origin = "*" + } + w.Header().Set("Access-Control-Allow-Origin", origin) + w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS") + w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization") + w.Header().Set("Access-Control-Allow-Credentials", "true") + + // Handle preflight requests + if r.Method == "OPTIONS" { + w.WriteHeader(http.StatusOK) + return + } + + next.ServeHTTP(w, r) + }) + } + + // Auth middleware + authMiddleware := func(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + // Skip auth for GET requests and OPTIONS + if r.Method == "GET" || r.Method == "OPTIONS" { + next.ServeHTTP(w, r) + return + } + + // Check for Authorization header + authHeader := r.Header.Get("Authorization") + if authHeader == "" { + http.Error(w, "Missing authorization token", http.StatusUnauthorized) + return + } + + // Verify token (in a real app, you would validate this against your auth system) + tokenParts := strings.Split(authHeader, " ") + if len(tokenParts) != 2 || tokenParts[0] != "Bearer" { + http.Error(w, "Invalid authorization header format", http.StatusUnauthorized) + return + } + + // In a real app, you would validate the token here + next.ServeHTTP(w, r) + }) + } + + // Apply CORS middleware to all routes + r.Use(corsMiddleware) + // Public routes r.PathPrefix("/kontakt/").Handler(http.StripPrefix("/kontakt", kontaktProxy)) r.PathPrefix("/uploads/").Handler(http.StripPrefix("/uploads/", http.FileServer(http.Dir("./uploads")))) @@ -76,9 +130,15 @@ func main() { // Authentication routes r.HandleFunc("/api/login", LoginHandler).Methods("POST", "OPTIONS") - // Protected API routes + // Public endpoints (must be defined before protected ones) + r.HandleFunc("/api/banner", GetBannerHandler).Methods("GET", "OPTIONS") + r.HandleFunc("/submit", handleSubmit).Methods("POST", "OPTIONS") // Public submit endpoint for evidence-aut.html + + // Protected API routes with auth middleware api := r.PathPrefix("/api").Subrouter() - api.Use(AuthMiddleware) + api.Use(authMiddleware) + + // Protected API endpoints api.HandleFunc("/submit", handleSubmit).Methods("POST") api.HandleFunc("/banner/update", UpdateBannerHandler).Methods("POST", "OPTIONS") @@ -89,27 +149,9 @@ func main() { api.HandleFunc("/apps/{id}", UpdateAppHandler).Methods("PUT") api.HandleFunc("/apps/{id}", DeleteAppHandler).Methods("DELETE") - // Public endpoints - r.HandleFunc("/api/banner", GetBannerHandler).Methods("GET", "OPTIONS") - - // Important: This public submit endpoint must be defined BEFORE the static file server - r.HandleFunc("/submit", handleSubmit).Methods("POST", "OPTIONS") // Public submit endpoint for evidence-aut.html - - // Add CORS middleware for API - r.Use(func(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Access-Control-Allow-Origin", "*") - w.Header().Set("Access-Control-Allow-Methods", "GET, POST, OPTIONS") - w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization") - - if r.Method == "OPTIONS" { - w.WriteHeader(http.StatusOK) - return - } - - next.ServeHTTP(w, r) - }) - }) + // Serve static files (must be the last route) + fs := http.FileServer(http.Dir(".")) + r.PathPrefix("/").Handler(fs) // Admin routes r.HandleFunc("/admin", func(w http.ResponseWriter, r *http.Request) { @@ -132,31 +174,12 @@ func main() { http.ServeFile(w, r, "evidence-aut.html") }).Methods("GET") + // Contact page route + r.HandleFunc("/kontakt", contactHandler).Methods("GET") + // Static file server for public files - must be the last route defined - fs := http.FileServer(http.Dir(".")) - r.PathPrefix("/").Handler(fs) - - r.HandleFunc("/kontakt", func(w http.ResponseWriter, r *http.Request) { - // Check if kontakt service is already running - resp, err := http.Get("http://webportal:8080/health") - if err == nil && resp.StatusCode == 200 { - http.Redirect(w, r, "http://webportal:8080/", http.StatusFound) - return - } - - // Start the service if not running - cmd := exec.Command("make", "dev") - cmd.Dir = "kontakt" - err = cmd.Start() - if err != nil { - http.Error(w, "Failed to start kontakt service", http.StatusInternalServerError) - return - } - - // Wait briefly for service to start - time.Sleep(2 * time.Second) - http.Redirect(w, r, "http://webportal:8080/", http.StatusFound) - }).Methods("GET") + fileServer := http.FileServer(http.Dir(".")) + r.PathPrefix("/").Handler(fileServer) // Apply CORS middleware to all routes handler := enableCORS(r) @@ -173,6 +196,29 @@ func main() { } } +// contactHandler handles the contact page request +func contactHandler(w http.ResponseWriter, r *http.Request) { + // Check if kontakt service is already running + resp, err := http.Get("http://webportal:8080/health") + if err == nil && resp.StatusCode == 200 { + http.Redirect(w, r, "http://webportal:8080/", http.StatusFound) + return + } + + // Start the service if not running + cmd := exec.Command("make", "dev") + cmd.Dir = "kontakt" + err = cmd.Start() + if err != nil { + http.Error(w, "Failed to start kontakt service", http.StatusInternalServerError) + return + } + + // Wait briefly for service to start + time.Sleep(2 * time.Second) + http.Redirect(w, r, "http://webportal:8080/", http.StatusFound) +} + func enableCORS(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Access-Control-Allow-Origin", "*") @@ -238,6 +284,24 @@ func saveApps(apps []App) error { // App Handlers func GetAppsHandler(w http.ResponseWriter, r *http.Request) { + // Set CORS headers + w.Header().Set("Access-Control-Allow-Origin", "*") + w.Header().Set("Access-Control-Allow-Methods", "GET, OPTIONS") + w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization") + + // Handle preflight requests + if r.Method == "OPTIONS" { + w.WriteHeader(http.StatusOK) + return + } + + // Only allow GET requests + if r.Method != "GET" { + http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) + return + } + + // Load apps from JSON file apps, err := loadApps() if err != nil { log.Printf("Error loading apps: %v", err) @@ -483,8 +547,9 @@ func UpdateAppHandler(w http.ResponseWriter, r *http.Request) { url := r.FormValue("url") description := r.FormValue("description") - // Handle file upload if a new file is provided var iconPath string + + // Handle file upload file, handler, err := r.FormFile("icon") if err == nil { defer file.Close()