function escapeHtml(value) {
return String(value)
.replace(/&/g, '&')
.replace(//g, '>')
.replace(/"/g, '"');
}
function renderNode(node) {
if (node.type === 'workflow') {
const badgeClass = node.active ? 'bg-success' : 'bg-secondary';
const badgeText = node.active ? '有効' : '無効';
return `
${escapeHtml(node.name)}
${badgeText}
`;
}
return `
📁 ${escapeHtml(node.name)}
${renderFolderTree(node.children)}
`;
}
function renderFolderTree(tree) {
if (!tree || tree.length === 0) return '';
return tree.map(renderNode).join('\n');
}
const EXECUTION_BADGE = {
success: 'bg-success',
error: 'bg-danger',
crashed: 'bg-danger',
running: 'bg-primary',
waiting: 'bg-warning text-dark',
canceled: 'bg-secondary',
new: 'bg-secondary',
unknown: 'bg-secondary',
};
const EXECUTION_STATUS_LABEL = {
success: '成功',
error: 'エラー',
crashed: 'クラッシュ',
running: '実行中',
waiting: '待機中',
canceled: 'キャンセル',
new: '新規',
unknown: '不明',
};
function formatJst(isoString) {
if (!isoString) return '';
const utc = new Date(isoString);
const jst = new Date(utc.getTime() + 9 * 60 * 60 * 1000);
const pad = (n) => String(n).padStart(2, '0');
return `${jst.getUTCFullYear()}-${pad(jst.getUTCMonth() + 1)}-${pad(jst.getUTCDate())} ${pad(jst.getUTCHours())}:${pad(jst.getUTCMinutes())}:${pad(jst.getUTCSeconds())}`;
}
function renderExecutionList(executions) {
if (!executions || executions.length === 0) {
return '実行履歴なし
';
}
const rows = executions
.map((e) => {
const badgeClass = EXECUTION_BADGE[e.status] || 'bg-secondary';
const statusLabel = EXECUTION_STATUS_LABEL[e.status] || e.status;
return `
| ${escapeHtml(formatJst(e.startedAt))} |
${escapeHtml(statusLabel)} |
`;
})
.join('\n');
return ``;
}
function renderExecutionDetail(execution) {
const runData = (execution.data && execution.data.resultData && execution.data.resultData.runData) || {};
const rows = Object.entries(runData)
.map(([nodeName, runs]) => {
const lastRun = runs[runs.length - 1] || {};
const hasError = Boolean(lastRun.error);
const badgeClass = hasError ? 'bg-danger' : 'bg-success';
const badgeText = hasError ? 'エラー' : '成功';
const errorMessage = hasError ? escapeHtml(lastRun.error.message || '') : '';
return `
| ${escapeHtml(nodeName)} |
${badgeText} |
${errorMessage} |
`;
})
.join('\n');
return ``;
}
function renderN8nPage(tree) {
const treeHtml = renderFolderTree(tree);
return `
n8nワークフロー
`;
}
module.exports = { renderFolderTree, renderExecutionList, renderExecutionDetail, renderN8nPage };