This commit is contained in:
johnpccd 2025-05-24 15:36:14 +02:00
parent d193f05974
commit 3208307eaa
10 changed files with 193 additions and 39 deletions

View File

@ -46,7 +46,7 @@
</a> </a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="#" onclick="showPage('api-keys')"> <a class="nav-link" href="#" onclick="showPage('apiKeys')">
<i class="fas fa-key me-1"></i>API Keys <i class="fas fa-key me-1"></i>API Keys
</a> </a>
</li> </li>

View File

@ -11,9 +11,11 @@ class ApiClient {
} }
getHeaders(includeContentType = true) { getHeaders(includeContentType = true) {
const headers = { const headers = {};
'X-API-Key': this.apiKey
}; if (this.apiKey) {
headers['X-API-Key'] = this.apiKey;
}
if (includeContentType) { if (includeContentType) {
headers['Content-Type'] = 'application/json'; headers['Content-Type'] = 'application/json';
@ -25,8 +27,8 @@ class ApiClient {
async makeRequest(method, endpoint, data = null, isFormData = false) { async makeRequest(method, endpoint, data = null, isFormData = false) {
this.updateConfig(); this.updateConfig();
if (!this.baseUrl || !this.apiKey) { if (!this.baseUrl) {
throw new Error('API not configured. Please set API base URL and key.'); throw new Error('API not configured. Please set API base URL.');
} }
const url = `${this.baseUrl}/api/v1${endpoint}`; const url = `${this.baseUrl}/api/v1${endpoint}`;

View File

@ -9,6 +9,7 @@ const app = {
// Initialize the application // Initialize the application
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', () => {
console.log('App.js DOMContentLoaded fired');
console.log('SeReact Frontend v' + app.version + ' - Initializing...'); console.log('SeReact Frontend v' + app.version + ' - Initializing...');
// Initialize configuration // Initialize configuration
@ -184,7 +185,7 @@ function handleRouteChange() {
const route = hash || 'home'; const route = hash || 'home';
// Validate route // Validate route
const validRoutes = ['home', 'config', 'images', 'search', 'teams', 'users', 'api-keys']; const validRoutes = ['home', 'config', 'images', 'search', 'teams', 'users', 'apiKeys'];
if (validRoutes.includes(route)) { if (validRoutes.includes(route)) {
showPage(route); showPage(route);
@ -248,7 +249,7 @@ function setupKeyboardShortcuts() {
// Number shortcuts for navigation // Number shortcuts for navigation
if (e.key >= '1' && e.key <= '6' && !e.ctrlKey && !e.metaKey && !e.altKey) { if (e.key >= '1' && e.key <= '6' && !e.ctrlKey && !e.metaKey && !e.altKey) {
const pages = ['home', 'images', 'search', 'teams', 'users', 'api-keys']; const pages = ['home', 'images', 'search', 'teams', 'users', 'apiKeys'];
const pageIndex = parseInt(e.key) - 1; const pageIndex = parseInt(e.key) - 1;
if (pages[pageIndex]) { if (pages[pageIndex]) {
e.preventDefault(); e.preventDefault();
@ -279,7 +280,7 @@ function refreshCurrentPageData() {
case 'users': case 'users':
loadUsers(); loadUsers();
break; break;
case 'api-keys': case 'apiKeys':
loadApiKeys(); loadApiKeys();
break; break;
} }

View File

@ -3,6 +3,11 @@ class Config {
constructor() { constructor() {
this.apiBaseUrl = localStorage.getItem('apiBaseUrl') || 'http://localhost:8000'; this.apiBaseUrl = localStorage.getItem('apiBaseUrl') || 'http://localhost:8000';
this.apiKey = localStorage.getItem('apiKey') || ''; this.apiKey = localStorage.getItem('apiKey') || '';
// Set default configuration if not already set
if (!localStorage.getItem('apiBaseUrl')) {
this.setApiBaseUrl('http://localhost:8000');
}
} }
setApiBaseUrl(url) { setApiBaseUrl(url) {
@ -24,7 +29,7 @@ class Config {
} }
isConfigured() { isConfigured() {
return this.apiBaseUrl && this.apiKey; return this.apiBaseUrl; // Only require base URL, make API key optional for now
} }
clear() { clear() {
@ -82,11 +87,6 @@ async function testConnection() {
return; return;
} }
if (!apiKey) {
showAlert('API Key is required', 'danger');
return;
}
const testButton = document.querySelector('button[onclick="testConnection()"]'); const testButton = document.querySelector('button[onclick="testConnection()"]');
const originalText = testButton.innerHTML; const originalText = testButton.innerHTML;
testButton.innerHTML = '<span class="loading-spinner"></span> Testing...'; testButton.innerHTML = '<span class="loading-spinner"></span> Testing...';
@ -105,28 +105,34 @@ async function testConnection() {
throw new Error(`Health check failed with status ${healthResponse.status}`); throw new Error(`Health check failed with status ${healthResponse.status}`);
} }
// Test API authentication // Test API authentication only if API key is provided
const authResponse = await fetch(`${apiBaseUrl}/api/v1/teams`, { if (apiKey) {
method: 'GET', const authResponse = await fetch(`${apiBaseUrl}/api/v1/teams`, {
headers: { method: 'GET',
'Content-Type': 'application/json', headers: {
'X-API-Key': apiKey 'Content-Type': 'application/json',
'X-API-Key': apiKey
}
});
if (authResponse.status === 401) {
throw new Error('Authentication failed - Invalid API key');
} else if (authResponse.status === 403) {
throw new Error('Access forbidden - API key lacks permissions');
} else if (!authResponse.ok) {
throw new Error(`API request failed with status ${authResponse.status}`);
} }
});
if (authResponse.status === 401) { showAlert('API connection successful! Backend is up and running with authentication.', 'success');
throw new Error('Authentication failed - Invalid API key'); } else {
} else if (authResponse.status === 403) { showAlert('API connection successful! Backend is up and running (no authentication tested).', 'success');
throw new Error('Access forbidden - API key lacks permissions');
} else if (!authResponse.ok) {
throw new Error(`API request failed with status ${authResponse.status}`);
} }
showAlert('API connection successful! Backend is up and running.', 'success');
// Save the working configuration // Save the working configuration
config.setApiBaseUrl(apiBaseUrl); config.setApiBaseUrl(apiBaseUrl);
config.setApiKey(apiKey); if (apiKey) {
config.setApiKey(apiKey);
}
// Update form values // Update form values
document.getElementById('apiBaseUrl').value = apiBaseUrl; document.getElementById('apiBaseUrl').value = apiBaseUrl;

View File

@ -2,6 +2,11 @@
// Load teams // Load teams
async function loadTeams() { async function loadTeams() {
console.log('loadTeams called');
console.log('Config check:', config.isConfigured());
console.log('API Base URL:', config.getApiBaseUrl());
console.log('API Key:', config.getApiKey() ? 'Set' : 'Not set');
if (!config.isConfigured()) { if (!config.isConfigured()) {
showAlert('Please configure your API settings first.', 'warning'); showAlert('Please configure your API settings first.', 'warning');
return; return;
@ -11,11 +16,15 @@ async function loadTeams() {
container.innerHTML = '<div class="text-center"><div class="loading-spinner"></div> Loading teams...</div>'; container.innerHTML = '<div class="text-center"><div class="loading-spinner"></div> Loading teams...</div>';
try { try {
console.log('Making API request to get teams...');
const response = await apiClient.getTeams(); const response = await apiClient.getTeams();
console.log('API response:', response);
// Handle structured response - extract teams array from response object // Handle structured response - extract teams array from response object
const teams = response.teams || response; const teams = response.teams || response;
console.log('Teams data:', teams);
displayTeams(teams); displayTeams(teams);
} catch (error) { } catch (error) {
console.error('Error loading teams:', error);
handleApiError(error, 'loading teams'); handleApiError(error, 'loading teams');
container.innerHTML = '<div class="alert alert-danger">Failed to load teams</div>'; container.innerHTML = '<div class="alert alert-danger">Failed to load teams</div>';
} }

View File

@ -2,14 +2,18 @@
// Page navigation // Page navigation
function showPage(pageId) { function showPage(pageId) {
console.log('showPage called with pageId:', pageId);
// Hide all pages // Hide all pages
const pages = document.querySelectorAll('.page'); const pages = document.querySelectorAll('.page');
console.log('Found pages:', pages.length);
pages.forEach(page => { pages.forEach(page => {
page.style.display = 'none'; page.style.display = 'none';
}); });
// Show the selected page // Show the selected page
const targetPage = document.getElementById(pageId + 'Page'); const targetPage = document.getElementById(pageId + 'Page');
console.log('Target page element:', targetPage);
if (targetPage) { if (targetPage) {
targetPage.style.display = 'block'; targetPage.style.display = 'block';
@ -20,7 +24,10 @@ function showPage(pageId) {
updateNavActiveState(pageId); updateNavActiveState(pageId);
// Load page data if needed // Load page data if needed
console.log('Loading page data for:', pageId);
loadPageData(pageId); loadPageData(pageId);
} else {
console.error('Target page not found:', pageId + 'Page');
} }
} }
@ -38,25 +45,34 @@ function updateNavActiveState(activePageId) {
// Load page-specific data // Load page-specific data
function loadPageData(pageId) { function loadPageData(pageId) {
console.log('loadPageData called with pageId:', pageId);
switch (pageId) { switch (pageId) {
case 'config': case 'config':
console.log('Loading config page');
initializeConfigForm(); initializeConfigForm();
break; break;
case 'images': case 'images':
console.log('Loading images page');
loadImages(); loadImages();
break; break;
case 'teams': case 'teams':
console.log('Loading teams page');
loadTeams(); loadTeams();
break; break;
case 'users': case 'users':
console.log('Loading users page');
loadUsers(); loadUsers();
break; break;
case 'api-keys': case 'apiKeys':
console.log('Loading apiKeys page');
loadApiKeys(); loadApiKeys();
break; break;
case 'search': case 'search':
console.log('Loading search page');
initializeSearchForm(); initializeSearchForm();
break; break;
default:
console.log('No specific loader for page:', pageId);
} }
} }
@ -266,6 +282,8 @@ window.addEventListener('hashchange', () => {
// Initialize page on load // Initialize page on load
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', () => {
console.log('UI.js DOMContentLoaded fired');
// Initialize tooltips and popovers // Initialize tooltips and popovers
initializeTooltips(); initializeTooltips();
initializePopovers(); initializePopovers();
@ -276,5 +294,25 @@ document.addEventListener('DOMContentLoaded', () => {
// Show initial page // Show initial page
const hash = window.location.hash.substring(1); const hash = window.location.hash.substring(1);
const initialPage = hash || 'home'; const initialPage = hash || 'home';
console.log('Initial page:', initialPage);
showPage(initialPage); showPage(initialPage);
}); });
// Test function for debugging
function testPageNavigation() {
console.log('Testing page navigation...');
console.log('Available pages:', document.querySelectorAll('.page').length);
const pages = ['home', 'config', 'teams', 'users'];
pages.forEach(pageId => {
const pageElement = document.getElementById(pageId + 'Page');
console.log(`Page ${pageId}:`, pageElement ? 'Found' : 'NOT FOUND');
});
// Test showing teams page
console.log('Testing showPage("teams")...');
showPage('teams');
}
// Make test function available globally
window.testPageNavigation = testPageNavigation;

View File

@ -2,6 +2,11 @@
// Load users // Load users
async function loadUsers() { async function loadUsers() {
console.log('loadUsers called');
console.log('Config check:', config.isConfigured());
console.log('API Base URL:', config.getApiBaseUrl());
console.log('API Key:', config.getApiKey() ? 'Set' : 'Not set');
if (!config.isConfigured()) { if (!config.isConfigured()) {
showAlert('Please configure your API settings first.', 'warning'); showAlert('Please configure your API settings first.', 'warning');
return; return;
@ -11,11 +16,15 @@ async function loadUsers() {
container.innerHTML = '<div class="text-center"><div class="loading-spinner"></div> Loading users...</div>'; container.innerHTML = '<div class="text-center"><div class="loading-spinner"></div> Loading users...</div>';
try { try {
console.log('Making API request to get users...');
const response = await apiClient.getUsers(); const response = await apiClient.getUsers();
console.log('API response:', response);
// Handle structured response - extract users array from response object // Handle structured response - extract users array from response object
const users = response.users || response; const users = response.users || response;
console.log('Users data:', users);
displayUsers(users); displayUsers(users);
} catch (error) { } catch (error) {
console.error('Error loading users:', error);
handleApiError(error, 'loading users'); handleApiError(error, 'loading users');
container.innerHTML = '<div class="alert alert-danger">Failed to load users</div>'; container.innerHTML = '<div class="alert alert-danger">Failed to load users</div>';
} }

View File

@ -11,7 +11,7 @@ import sys
from pathlib import Path from pathlib import Path
# Configuration # Configuration
PORT = 8080 PORT = 8081
HOST = 'localhost' HOST = 'localhost'
class CustomHTTPRequestHandler(http.server.SimpleHTTPRequestHandler): class CustomHTTPRequestHandler(http.server.SimpleHTTPRequestHandler):

92
client/test.html Normal file
View File

@ -0,0 +1,92 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SeReact Test</title>
<style>
.page { display: none; padding: 20px; border: 1px solid #ccc; margin: 10px; }
.page.active { display: block; }
button { margin: 5px; padding: 10px; }
</style>
</head>
<body>
<h1>SeReact Navigation Test</h1>
<div>
<button onclick="showPage('home')">Home</button>
<button onclick="showPage('teams')">Teams</button>
<button onclick="showPage('users')">Users</button>
<button onclick="showPage('config')">Config</button>
<button onclick="testPageNavigation()">Test Navigation</button>
</div>
<div id="homePage" class="page">
<h2>Home Page</h2>
<p>This is the home page content.</p>
</div>
<div id="teamsPage" class="page">
<h2>Teams Page</h2>
<div id="teamsContainer">Teams will load here...</div>
</div>
<div id="usersPage" class="page">
<h2>Users Page</h2>
<div id="usersContainer">Users will load here...</div>
</div>
<div id="configPage" class="page">
<h2>Config Page</h2>
<p>Configuration settings go here.</p>
</div>
<div id="alertContainer"></div>
<script>
// Simple config mock
const config = {
isConfigured: () => true,
getApiBaseUrl: () => 'http://localhost:8000',
getApiKey: () => 'test-key'
};
// Simple API client mock
const apiClient = {
getTeams: () => Promise.resolve({teams: [{id: '1', name: 'Test Team', description: 'A test team', created_at: new Date().toISOString()}]}),
getUsers: () => Promise.resolve({users: [{id: '1', name: 'Test User', email: 'test@example.com', team_id: '1', is_admin: false, created_at: new Date().toISOString()}]})
};
// Mock functions
function showAlert(message, type) {
console.log(`Alert (${type}): ${message}`);
}
function handleApiError(error, context) {
console.error(`API Error ${context}:`, error);
}
function escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
function formatDate(dateString) {
return new Date(dateString).toLocaleString();
}
</script>
<script src="js/ui.js"></script>
<script src="js/teams.js"></script>
<script src="js/users.js"></script>
<script>
// Initialize on load
document.addEventListener('DOMContentLoaded', () => {
console.log('Test page loaded');
showPage('home');
});
</script>
</body>
</html>

View File

@ -72,10 +72,7 @@ start_dev_server() {
fi fi
# Check if Python is available # Check if Python is available
if command -v python3 &> /dev/null; then if command -v python &> /dev/null; then
print_color $BLUE "🐍 Using Python development server"
python3 serve.py
elif command -v python &> /dev/null; then
print_color $BLUE "🐍 Using Python development server" print_color $BLUE "🐍 Using Python development server"
python serve.py python serve.py
else else