const $ = (s) => document.querySelector(s); const esc = (s) => { const d = document.createElement('div'); d.textContent = s ?? ''; return d.innerHTML; }; function toast(msg, kind = '') { const t = $('#toast'); t.textContent = msg; t.className = 'toast show ' + kind; setTimeout(() => t.classList.remove('show'), 3500); } function formatDate(s) { if (!s) return '--'; return new Date(s).toLocaleDateString(); } function statusBadge(status) { const cls = status === 'valid' ? 'succeeded' : status === 'expiring' ? 'pending' : status === 'expired' ? 'failed' : 'pending'; return `${status}`; } function methodBadge(method) { if (method === 'ad-hoc') return 'ad-hoc'; if (method === 'development') return 'dev'; if (method === 'app-store') return 'app-store'; if (method === 'enterprise') return 'enterprise'; return `${method || 'unknown'}`; } // --- Installed profiles --- let installedProfiles = []; async function loadProfiles() { const r = await fetch('/api/profiles'); if (r.status === 401) { location.href = '/login'; return; } installedProfiles = await r.json(); const container = $('#profiles-container'); if (!installedProfiles.length) { container.innerHTML = '

No provisioning profiles installed. Generate one from the bundle IDs above.

'; return; } // Only show ad-hoc profiles const adhoc = installedProfiles.filter(p => p.method === 'ad-hoc'); if (!adhoc.length) { container.innerHTML = '

No ad-hoc profiles found. Other profile types are installed but not shown.

'; return; } container.innerHTML = ` ${adhoc.map(p => ` `).join('')}
Bundle ID Name Devices Expires Status
${esc(p.bundleIdentifier || '--')} ${esc(p.name || '--')} ${p.deviceCount ?? '--'} ${esc(formatDate(p.expiresAt))} ${statusBadge(p.status)} ${p.bundleIdentifier ? `` : ''}
`; bindProfileActions(container); } function bindProfileActions(container) { container.querySelectorAll('.delete-btn').forEach((btn) => { btn.addEventListener('click', async () => { if (!confirm('Delete this profile?')) return; btn.disabled = true; btn.textContent = '...'; try { const r = await fetch(`/api/profiles/${btn.dataset.uuid}`, { method: 'DELETE' }); if (!r.ok) throw new Error((await r.json()).error || 'Delete failed'); toast('Profile deleted', 'success'); loadProfiles(); loadBundleIds(); } catch (err) { toast(err.message, 'error'); btn.disabled = false; btn.textContent = 'Delete'; } }); }); container.querySelectorAll('.regen-btn').forEach((btn) => { btn.addEventListener('click', () => generateProfile(btn.dataset.bundle, null, btn)); }); } async function generateProfile(bundleId, teamId, btn) { if (!teamId) { // Regenerate button on the installed-profiles table doesn't know the team yet — look it up const installed = installedProfiles.find(p => p.bundleIdentifier === bundleId); teamId = installed?.teamId; } if (!teamId) { toast('No team ID available for this bundle. Generate from the ASC bundle list above.', 'error'); return; } const origText = btn.textContent; btn.disabled = true; btn.textContent = 'Generating...'; try { const r = await fetch('/api/profiles/generate', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ bundleId, teamId }), }); if (!r.ok) throw new Error((await r.json()).error || 'Generation failed'); toast(`Profile generated for ${bundleId}`, 'success'); loadProfiles(); loadBundleIds(); } catch (err) { toast(err.message, 'error'); } finally { btn.disabled = false; btn.textContent = origText; } } // --- Bundle IDs from ASC --- async function loadBundleIds() { const container = $('#bundle-ids-container'); try { const r = await fetch('/api/bundle-ids'); if (r.status === 401) { location.href = '/login'; return; } const groups = await r.json(); if (groups.error) throw new Error(groups.error); if (!Array.isArray(groups) || !groups.length) { container.innerHTML = '

No developer accounts configured. Add one in Settings.

'; return; } const installedBundles = new Set( installedProfiles .filter(p => p.method === 'ad-hoc') .map(p => p.bundleIdentifier) ); container.innerHTML = groups.map(g => { if (g.error) { return `

${esc(g.teamName)} ${esc(g.teamId)}

${esc(g.error)}

`; } if (!g.bundleIds.length) { return `

${esc(g.teamName)} ${esc(g.teamId)}

No bundle IDs registered.

`; } return `

${esc(g.teamName)} ${esc(g.teamId)}

${g.bundleIds.map(b => { const hasProfile = installedBundles.has(b.identifier); return `
${esc(b.identifier)} ${esc(b.name)} ${esc(b.platform)}
${hasProfile ? 'has profile' : `` }
`; }).join('')}
`; }).join(''); container.querySelectorAll('.gen-btn').forEach((btn) => { btn.addEventListener('click', () => generateProfile(btn.dataset.bundle, btn.dataset.team, btn)); }); } catch (err) { container.innerHTML = `

${esc(err.message)}

Configure at least one Developer Account in Settings to fetch bundle IDs.

`; } } // Load both in parallel loadProfiles().then(() => loadBundleIds());