const { listComposes, getComposeEnv, getContainers } = require('./dokployClient'); const { extractPortalMeta } = require('./portalMeta'); function escapeHtml(value) { return String(value) .replace(/&/g, '&') .replace(//g, '>') .replace(/"/g, '"'); } async function buildCards(environmentId) { const composes = await listComposes(environmentId); const settled = await Promise.allSettled( composes.map(async (c) => { const { env, appName } = await getComposeEnv(c.composeId); const meta = extractPortalMeta(env, c.name); if (!meta) return null; let containerState = 'unknown'; try { const containers = await getContainers(appName); containerState = containers[0]?.state || 'stopped'; } catch { containerState = 'unknown'; } return { composeId: c.composeId, appName, composeStatus: c.composeStatus, containerState, meta }; }) ); const cards = []; settled.forEach((result, i) => { if (result.status === 'fulfilled') { if (result.value) cards.push(result.value); } else { cards.push({ error: true, composeId: composes[i].composeId, name: composes[i].name }); } }); return cards; } function renderCard(card) { if (card.error) { return `

${escapeHtml(card.name || card.composeId)}

状態取得失敗

`; } const { composeId, composeStatus, containerState, meta } = card; const openLink = meta.appType === 'web' && meta.url ? `開く` : ''; const actionButtons = meta.appType === 'web' ? ` ` : ``; return `

${escapeHtml(meta.label)}

compose: ${escapeHtml(composeStatus)} / container: ${escapeHtml(containerState)}

${openLink} ${actionButtons}
`; } async function renderDashboard(environmentId) { const cards = await buildCards(environmentId); const cardsHtml = cards.map(renderCard).join('\n'); return ` App Portal

App Portal

${cardsHtml}
`; } module.exports = { buildCards, renderCard, renderDashboard };