cp
This commit is contained in:
parent
d193f05974
commit
3208307eaa
@ -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>
|
||||||
|
|||||||
@ -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}`;
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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>';
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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;
|
||||||
@ -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>';
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
92
client/test.html
Normal 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>
|
||||||
@ -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
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user