const API_BASE = '/api'; let currentUser = null; // Check authentication on load async function init() { await checkAuth(); setupEventListeners(); if (currentUser) { showMainPage(); loadJobs(); loadRunners(); } else { showLoginPage(); } } async function checkAuth() { try { const response = await fetch(`${API_BASE}/auth/me`); if (response.ok) { currentUser = await response.json(); return true; } } catch (error) { console.error('Auth check failed:', error); } return false; } function showLoginPage() { document.getElementById('login-page').classList.remove('hidden'); document.getElementById('main-page').classList.add('hidden'); } function showMainPage() { document.getElementById('login-page').classList.add('hidden'); document.getElementById('main-page').classList.remove('hidden'); if (currentUser) { document.getElementById('user-name').textContent = currentUser.name || currentUser.email; } } function setupEventListeners() { // Navigation document.querySelectorAll('.nav-btn').forEach(btn => { btn.addEventListener('click', (e) => { const page = e.target.dataset.page; switchPage(page); }); }); // Logout document.getElementById('logout-btn').addEventListener('click', async () => { await fetch(`${API_BASE}/auth/logout`, { method: 'POST' }); currentUser = null; showLoginPage(); }); // Job form document.getElementById('job-form').addEventListener('submit', async (e) => { e.preventDefault(); await submitJob(); }); } function switchPage(page) { document.querySelectorAll('.content-page').forEach(p => p.classList.add('hidden')); document.querySelectorAll('.nav-btn').forEach(b => b.classList.remove('active')); document.getElementById(`${page}-page`).classList.remove('hidden'); document.querySelector(`[data-page="${page}"]`).classList.add('active'); if (page === 'jobs') { loadJobs(); } else if (page === 'runners') { loadRunners(); } } async function submitJob() { const form = document.getElementById('job-form'); const formData = new FormData(form); const jobData = { name: document.getElementById('job-name').value, frame_start: parseInt(document.getElementById('frame-start').value), frame_end: parseInt(document.getElementById('frame-end').value), output_format: document.getElementById('output-format').value, }; try { // Create job const jobResponse = await fetch(`${API_BASE}/jobs`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(jobData), }); if (!jobResponse.ok) { throw new Error('Failed to create job'); } const job = await jobResponse.json(); // Upload file const fileInput = document.getElementById('blend-file'); if (fileInput.files.length > 0) { const fileFormData = new FormData(); fileFormData.append('file', fileInput.files[0]); const fileResponse = await fetch(`${API_BASE}/jobs/${job.id}/upload`, { method: 'POST', body: fileFormData, }); if (!fileResponse.ok) { throw new Error('Failed to upload file'); } } alert('Job submitted successfully!'); form.reset(); switchPage('jobs'); loadJobs(); } catch (error) { alert('Failed to submit job: ' + error.message); } } async function loadJobs() { try { const response = await fetch(`${API_BASE}/jobs`); if (!response.ok) throw new Error('Failed to load jobs'); const jobs = await response.json(); displayJobs(jobs); } catch (error) { console.error('Failed to load jobs:', error); } } function displayJobs(jobs) { const container = document.getElementById('jobs-list'); if (jobs.length === 0) { container.innerHTML = '
No jobs yet. Submit a job to get started!
'; return; } container.innerHTML = jobs.map(job => `No runners connected.
'; return; } container.innerHTML = runners.map(runner => { const lastHeartbeat = new Date(runner.last_heartbeat); const isOnline = (Date.now() - lastHeartbeat.getTime()) < 60000; // 1 minute return `