mirror of
https://github.com/Dvorinka/PPve.git
synced 2026-06-03 20:12:59 +00:00
fef
This commit is contained in:
+47
-32
@@ -1459,15 +1459,36 @@ function loadHardcodedApps() {
|
|||||||
|
|
||||||
// Load dynamic apps
|
// Load dynamic apps
|
||||||
async function loadDynamicApps() {
|
async function loadDynamicApps() {
|
||||||
const dynamicAppsList = document.getElementById('dynamicAppsList');
|
console.log("Loading dynamic apps...");
|
||||||
|
const dynamicAppsContainer = document.getElementById('dynamicApps');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch('/api/apps');
|
const token = localStorage.getItem('token');
|
||||||
if (!response.ok) throw new Error('Nepodařilo se načíst seznam aplikací');
|
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();
|
const apps = await response.json();
|
||||||
|
console.log("Loaded dynamic apps:", apps);
|
||||||
|
|
||||||
if (apps.length === 0) {
|
if (!Array.isArray(apps) || apps.length === 0) {
|
||||||
dynamicAppsList.innerHTML = `
|
dynamicAppsList.innerHTML = `
|
||||||
<div class="text-center py-8">
|
<div class="text-center py-8">
|
||||||
<i class="fas fa-inbox text-4xl text-gray-300 mb-2"></i>
|
<i class="fas fa-inbox text-4xl text-gray-300 mb-2"></i>
|
||||||
@@ -1477,7 +1498,8 @@ async function loadDynamicApps() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
dynamicAppsList.innerHTML = apps
|
// Filter out hardcoded apps and map to HTML
|
||||||
|
const dynamicApps = apps
|
||||||
.filter(app => !app.id || !app.id.startsWith('hardcoded-'))
|
.filter(app => !app.id || !app.id.startsWith('hardcoded-'))
|
||||||
.map(app => `
|
.map(app => `
|
||||||
<div class="bg-white rounded-lg shadow p-4 flex items-center justify-between" data-app-id="${app.id}">
|
<div class="bg-white rounded-lg shadow p-4 flex items-center justify-between" data-app-id="${app.id}">
|
||||||
@@ -1489,43 +1511,34 @@ async function loadDynamicApps() {
|
|||||||
</div>`
|
</div>`
|
||||||
}
|
}
|
||||||
<div>
|
<div>
|
||||||
<h4 class="font-medium">${app.name}</h4>
|
<h4 class="font-medium">${app.name || 'Neznámá aplikace'}</h4>
|
||||||
<p class="text-sm text-gray-500">${app.url}</p>
|
<p class="text-sm text-gray-500">${app.url || ''}</p>
|
||||||
${app.description ? `<p class="text-sm text-gray-400">${app.description}</p>` : ''}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex space-x-2">
|
<div class="flex space-x-2">
|
||||||
<button class="edit-app-btn p-2 text-blue-500 hover:text-blue-700" data-app-id="${app.id}">
|
<button onclick="editApp('${app.id}')" class="text-blue-500 hover:text-blue-700">
|
||||||
<i class="fas fa-edit"></i>
|
<i class="fas fa-edit"></i>
|
||||||
</button>
|
</button>
|
||||||
<button class="delete-app-btn p-2 text-red-500 hover:text-red-700" data-app-id="${app.id}">
|
<button onclick="deleteApp('${app.id}')" class="text-red-500 hover:text-red-700">
|
||||||
<i class="fas fa-trash"></i>
|
<i class="fas fa-trash"></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`).join('');
|
`).join('');
|
||||||
|
|
||||||
// Add event listeners to buttons
|
if (dynamicApps.length > 0) {
|
||||||
document.querySelectorAll('.edit-app-btn').forEach(btn => {
|
dynamicAppsList.innerHTML = dynamicApps;
|
||||||
btn.addEventListener('click', (e) => {
|
} else {
|
||||||
e.stopPropagation();
|
dynamicAppsList.innerHTML = `
|
||||||
const appId = btn.dataset.appId;
|
<div class="text-center py-8">
|
||||||
editApp(appId);
|
<i class="fas fa-inbox text-4xl text-gray-300 mb-2"></i>
|
||||||
});
|
<p class="text-gray-500">Žádné vlastní aplikace nebyly nalezeny</p>
|
||||||
});
|
</div>
|
||||||
|
`;
|
||||||
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);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Chyba při načítání vlastních aplikací:', error);
|
console.error('Error loading dynamic apps:', error);
|
||||||
dynamicAppsList.innerHTML = `
|
dynamicAppsList.innerHTML = `
|
||||||
<div class="bg-red-50 border-l-4 border-red-400 p-4">
|
<div class="bg-red-50 border-l-4 border-red-400 p-4">
|
||||||
<div class="flex">
|
<div class="flex">
|
||||||
@@ -1533,7 +1546,9 @@ async function loadDynamicApps() {
|
|||||||
<i class="fas fa-exclamation-circle text-red-400"></i>
|
<i class="fas fa-exclamation-circle text-red-400"></i>
|
||||||
</div>
|
</div>
|
||||||
<div class="ml-3">
|
<div class="ml-3">
|
||||||
<p class="text-sm text-red-700">Chyba při načítání vlastních aplikací: ${error.message}</p>
|
<p class="text-sm text-red-700">
|
||||||
|
Chyba při načítání aplikací: ${error.message}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -65,6 +65,60 @@ func main() {
|
|||||||
kontaktURL, _ := url.Parse("http://webportal:8080")
|
kontaktURL, _ := url.Parse("http://webportal:8080")
|
||||||
kontaktProxy := httputil.NewSingleHostReverseProxy(kontaktURL)
|
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
|
// Public routes
|
||||||
r.PathPrefix("/kontakt/").Handler(http.StripPrefix("/kontakt", kontaktProxy))
|
r.PathPrefix("/kontakt/").Handler(http.StripPrefix("/kontakt", kontaktProxy))
|
||||||
r.PathPrefix("/uploads/").Handler(http.StripPrefix("/uploads/", http.FileServer(http.Dir("./uploads"))))
|
r.PathPrefix("/uploads/").Handler(http.StripPrefix("/uploads/", http.FileServer(http.Dir("./uploads"))))
|
||||||
@@ -76,9 +130,15 @@ func main() {
|
|||||||
// Authentication routes
|
// Authentication routes
|
||||||
r.HandleFunc("/api/login", LoginHandler).Methods("POST", "OPTIONS")
|
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 := r.PathPrefix("/api").Subrouter()
|
||||||
api.Use(AuthMiddleware)
|
api.Use(authMiddleware)
|
||||||
|
|
||||||
|
// Protected API endpoints
|
||||||
api.HandleFunc("/submit", handleSubmit).Methods("POST")
|
api.HandleFunc("/submit", handleSubmit).Methods("POST")
|
||||||
api.HandleFunc("/banner/update", UpdateBannerHandler).Methods("POST", "OPTIONS")
|
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}", UpdateAppHandler).Methods("PUT")
|
||||||
api.HandleFunc("/apps/{id}", DeleteAppHandler).Methods("DELETE")
|
api.HandleFunc("/apps/{id}", DeleteAppHandler).Methods("DELETE")
|
||||||
|
|
||||||
// Public endpoints
|
// Serve static files (must be the last route)
|
||||||
r.HandleFunc("/api/banner", GetBannerHandler).Methods("GET", "OPTIONS")
|
fs := http.FileServer(http.Dir("."))
|
||||||
|
r.PathPrefix("/").Handler(fs)
|
||||||
// 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)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
// Admin routes
|
// Admin routes
|
||||||
r.HandleFunc("/admin", func(w http.ResponseWriter, r *http.Request) {
|
r.HandleFunc("/admin", func(w http.ResponseWriter, r *http.Request) {
|
||||||
@@ -132,31 +174,12 @@ func main() {
|
|||||||
http.ServeFile(w, r, "evidence-aut.html")
|
http.ServeFile(w, r, "evidence-aut.html")
|
||||||
}).Methods("GET")
|
}).Methods("GET")
|
||||||
|
|
||||||
|
// Contact page route
|
||||||
|
r.HandleFunc("/kontakt", contactHandler).Methods("GET")
|
||||||
|
|
||||||
// Static file server for public files - must be the last route defined
|
// Static file server for public files - must be the last route defined
|
||||||
fs := http.FileServer(http.Dir("."))
|
fileServer := http.FileServer(http.Dir("."))
|
||||||
r.PathPrefix("/").Handler(fs)
|
r.PathPrefix("/").Handler(fileServer)
|
||||||
|
|
||||||
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")
|
|
||||||
|
|
||||||
// Apply CORS middleware to all routes
|
// Apply CORS middleware to all routes
|
||||||
handler := enableCORS(r)
|
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 {
|
func enableCORS(next http.Handler) http.Handler {
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
w.Header().Set("Access-Control-Allow-Origin", "*")
|
w.Header().Set("Access-Control-Allow-Origin", "*")
|
||||||
@@ -238,6 +284,24 @@ func saveApps(apps []App) error {
|
|||||||
|
|
||||||
// App Handlers
|
// App Handlers
|
||||||
func GetAppsHandler(w http.ResponseWriter, r *http.Request) {
|
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()
|
apps, err := loadApps()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Error loading apps: %v", err)
|
log.Printf("Error loading apps: %v", err)
|
||||||
@@ -483,8 +547,9 @@ func UpdateAppHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
url := r.FormValue("url")
|
url := r.FormValue("url")
|
||||||
description := r.FormValue("description")
|
description := r.FormValue("description")
|
||||||
|
|
||||||
// Handle file upload if a new file is provided
|
|
||||||
var iconPath string
|
var iconPath string
|
||||||
|
|
||||||
|
// Handle file upload
|
||||||
file, handler, err := r.FormFile("icon")
|
file, handler, err := r.FormFile("icon")
|
||||||
if err == nil {
|
if err == nil {
|
||||||
defer file.Close()
|
defer file.Close()
|
||||||
|
|||||||
Reference in New Issue
Block a user