import { getAccessToken } from './api.js'; const API_BASE = '/api/v1/sync'; const SYNC_INTERVAL = 30000; // 30 seconds let _syncTimer = null; async function apiFetch(method, path, body = null) { const token = getAccessToken(); if (!token) return null; const res = await fetch(`${API_BASE}${path}`, { method, headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}`, }, credentials: 'include', body: body ? JSON.stringify(body) : null, }); if (!res.ok) return null; return res.json(); } // ==================== Preset Sync ==================== export async function syncPresets() { if (!getAccessToken()) return; try { // Get local presets const localRaw = localStorage.getItem('reaktor_presets'); const localPresets = localRaw ? JSON.parse(localRaw) : []; // Get server presets const serverData = await apiFetch('GET', '/presets'); if (!serverData) return; // Push local presets to server if (localPresets.length > 0) { await apiFetch('PUT', '/presets', { presets: localPresets.map(p => ({ name: p.name, data: p.data, updatedAt: p.savedAt || new Date().toISOString(), })), }); } // Merge server presets into local (add any missing) const serverPresets = serverData.presets || []; const localNames = new Set(localPresets.map(p => p.name)); let merged = [...localPresets]; for (const sp of serverPresets) { if (!localNames.has(sp.name)) { merged.push({ name: sp.name, data: sp.data, savedAt: sp.updatedAt, }); } } localStorage.setItem('reaktor_presets', JSON.stringify(merged)); } catch (err) { console.warn('[sync] preset sync failed:', err.message); } } // ==================== Game Progress Sync ==================== export async function syncProgress() { if (!getAccessToken()) return; try { const localRaw = localStorage.getItem('synthquest-progress'); const localProgress = localRaw ? JSON.parse(localRaw) : null; // Get server progress const serverData = await apiFetch('GET', '/progress'); if (localProgress) { // Push local to server await apiFetch('PUT', '/progress', { data: localProgress, updatedAt: new Date().toISOString(), }); } // If server has progress and local doesn't, pull it if (serverData?.progress && !localProgress) { localStorage.setItem('synthquest-progress', JSON.stringify(serverData.progress)); } // Also sync hint data const hintsRaw = localStorage.getItem('synthquest-hints'); // Hints are local-only for now (anti-cheat integrity) } catch (err) { console.warn('[sync] progress sync failed:', err.message); } } // ==================== Auto Sync ==================== export function startAutoSync() { if (_syncTimer) return; // Initial sync syncPresets(); syncProgress(); // Periodic sync _syncTimer = setInterval(() => { syncPresets(); syncProgress(); }, SYNC_INTERVAL); // Sync on tab focus document.addEventListener('visibilitychange', () => { if (document.visibilityState === 'visible') { syncPresets(); syncProgress(); } }); } export function stopAutoSync() { if (_syncTimer) { clearInterval(_syncTimer); _syncTimer = null; } }