[SYSTEM] Ready. Click the "R // 渲染状态
async function loadStatus() {
let isStaticMode = false;
let data = [];
try {
const res = await fetch('/api/status');
if (!res.ok) {
throw new Error(`HTTP error! status: ${res.status}`);
}
data = await res.json();
} catch (err) {
console.warn('后端管理 API 路由未响应,进入静态预览模式。正在拉取 products_data.json...', err);
isStaticMode = true;
try {
const res = await fetch('./products_data.json');
if (!res.ok) {
throw new Error(`HTTP error! status: ${res.status}`);
}
const rawProducts = await res.json();
// 在静态只读模式下,由于 PDF/ZIP 是离线预打包生成的,我们假定它们全部可用
data = rawProducts.map(product => ({
folder: product.folder,
title: product.title,
sku: product.sku,
price: product.price_usd,
niche: product.extensions.nicheN,
hasPdf: true,
hasZip: true,
prompts: product.brief_description,
emailSwipe: product.affiliate.promo_email_swipe
}));
} catch (staticErr) {
console.error('静态数据加载也失败了:', staticErr);
const container = document.getElementById('products-container');
container.innerHTML = `
加载产品套件数据失败: ${staticErr.message}
`;
return;
}
}
// 如果是静态模式,调整 UI 呈现
const rebuildBtn = document.querySelector('.btn-rebuild');
if (isStaticMode && rebuildBtn) {
rebuildBtn.style.opacity = '0.6';
rebuildBtn.style.cursor = 'not-allowed';
rebuildBtn.title = '重建功能仅在本地 Node.js 运行环境中可用';
// 避免重复添加提示
if (!document.getElementById('static-mode-badge')) {
const badge = document.createElement('span');
badge.id = 'static-mode-badge';
badge.style.fontSize = '12px';
badge.style.color = '#94a3b8';
badge.style.marginLeft = '12px';
badge.style.background = 'rgba(255,255,255,0.05)';
badge.style.padding = '4px 8px';
badge.style.borderRadius = '4px';
badge.style.border = '1px solid rgba(255,255,255,0.08)';
badge.innerText = '● Production (静态只读模式)';
rebuildBtn.parentNode.insertBefore(badge, rebuildBtn.nextSibling);
}
}
const container = document.getElementById('products-container');
container.innerHTML = '';
data.forEach(p => {
const card = document.createElement('div');
card.className = 'product-card';
const colors = getColorHex(p.folder);
card.style.borderLeft = `4px solid ${colors.hex}`;
const pdfBadge = p.hasPdf
? `
eBook PDF: OK
`
: `
eBook PDF: Not Built
`;
const zipBadge = p.hasZip
? `
ZIP Bundle: OK
`
: `
ZIP Bundle: Not Built
`;
card.innerHTML = `
Product Target Prompts / SOP Overview
${p.prompts}
Affiliate JV Email Swipes
${p.emailSwipe}
`;
container.appendChild(card);
});
}
// 映射颜色
function getColorHex(folder) {
const schemes = {
'01_copywriting_prompt_vault': { hex: '#10b981', rgb: '16, 185, 129' },
'02_faceless_youtube_blueprint': { hex: '#ef4444', rgb: '239, 68, 68' },
'03_childrens_book_kdp': { hex: '#a855f7', rgb: '168, 85, 247' },
'04_nocode_automation_agency': { hex: '#3b82f6', rgb: '59, 130, 246' },
'05_custom_gpts_monetization': { hex: '#eab308', rgb: '234, 179, 8' },
'06_etsy_digital_art': { hex: '#ec4899', rgb: '236, 72, 153' },
'07_ecom_seo_description': { hex: '#06b6d4', rgb: '6, 182, 212' },
'08_fiction_generation_kindle': { hex: '#f97316', rgb: '249, 115, 22' },
'09_sme_chatbot_sop': { hex: '#14b8a6', rgb: '20, 184, 166' },
'10_astrology_tarot_reading': { hex: '#6366f1', rgb: '99, 102, 241' }
};
return schemes[folder] || { hex: '#3b82f6', rgb: '59, 130, 246' };
}
// 展开栏控制
function toggleExpand(folder) {
const box = document.getElementById(`expand-${folder}`);
const isVisible = window.getComputedStyle(box).display === 'block';
box.style.display = isVisible ? 'none' : 'block';
}
// 复制功能
function copyText(elementId) {
const txt = document.getElementById(elementId).innerText;
navigator.clipboard.writeText(txt).then(() => {
alert('Copied to clipboard!');
});
}
// 触发重建打包
function rebuildSuite() {
// 拦截静态环境下的非法重建
if (document.getElementById('static-mode-badge')) {
alert('Notice: Rebuilding the products suite is only available in local development mode (running Node.js server.js). Cloudflare Pages hosting is read-only.');
return;
}
const terminal = document.getElementById('terminal-log');
terminal.innerText = '';
const source = new EventSource('/api/build');
terminal.innerText += `[SYSTEM] 连接打包控制端,拉取日志流中...\n`;
source.onmessage = function(event) {
terminal.innerText += event.data + '\n';
const body = document.querySelector('.terminal-body');
body.scrollTop = body.scrollHeight;
if (event.data.includes('[SERVER] 打包进程结束')) {
source.close();
loadStatus();
}
};
source.onerror = function() {
terminal.innerText += `\n[ERR] 连接被服务器中断或完成。正在刷新状态...\n`;
source.close();
loadStatus();
};
}
window.onload = loadStatus;