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 = `
| Bundle ID |
Name |
Devices |
Expires |
Status |
|
${adhoc.map(p => `
| ${esc(p.bundleIdentifier || '--')} |
${esc(p.name || '--')} |
${p.deviceCount ?? '--'} |
${esc(formatDate(p.expiresAt))} |
${statusBadge(p.status)} |
${p.bundleIdentifier ? `` : ''}
|
`).join('')}
`;
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());