/**
* Clone Architect — Deploy Site to /opt/clone-architect-site
*
* 1. Génère public/index.html via generate-site.ts
* 2. Copie public/index.html → /opt/clone-architect-site/index.html
* 3. Copie chaque extractions/{domain}/showcase/index.html → /opt/clone-architect-site/brands/{domain}/index.html
* 4. Crée /opt/clone-architect-site/404.html
* 5. Affiche le résumé du déploiement
*
* Usage: npx tsx scripts/deploy-site.ts
*/
import { readFileSync, writeFileSync, mkdirSync, existsSync, copyFileSync } from 'fs';
import { join, dirname } from 'path';
import { execSync } from 'child_process';
const ROOT = process.cwd();
const DEPLOY_DIR = '/opt/clone-architect-site';
const CATALOG_PATH = join(ROOT, 'catalog', 'index.json');
const PUBLIC_INDEX = join(ROOT, 'public', 'index.html');
function writeWithSudo(targetPath: string, content: string): void {
// Try direct write first (paul owns /opt/clone-architect-site)
try {
const dir = dirname(targetPath);
mkdirSync(dir, { recursive: true });
writeFileSync(targetPath, content);
} catch {
// Fallback: sudo node write
const escaped = content.replace(/\\/g, '\\\\').replace(/`/g, '\\`').replace(/\$/g, '\\$');
execSync(`sudo /usr/bin/node -e "
require('fs').mkdirSync(require('path').dirname('${targetPath}'), {recursive:true});
require('fs').writeFileSync('${targetPath}', \\\`${escaped}\\\`);
"`, { stdio: 'ignore' });
}
}
function copyWithSudo(src: string, dest: string): void {
try {
const dir = dirname(dest);
mkdirSync(dir, { recursive: true });
copyFileSync(src, dest);
} catch {
const dir = dirname(dest);
execSync(`sudo /usr/bin/node -e "
require('fs').mkdirSync('${dir}', {recursive:true});
require('fs').copyFileSync('${src}', '${dest}');
"`, { stdio: 'ignore' });
}
}
async function main() {
console.log('🚀 Clone Architect — Deploy Site\n');
// Step 1 — Regenerate public/index.html
console.log('📄 Génération public/index.html...');
execSync('npx tsx scripts/generate-site.ts', { cwd: ROOT, stdio: 'inherit' });
// Step 2 — Ensure deploy dir exists
try {
mkdirSync(DEPLOY_DIR, { recursive: true });
} catch {
execSync(`sudo /usr/bin/node -e "require('fs').mkdirSync('${DEPLOY_DIR}', {recursive:true})"`, { stdio: 'ignore' });
}
// Step 2b — Generate og-image.png (non-fatal: SEO/social-preview only, must NOT abort deploy).
// A transient Playwright screenshot failure here previously killed the whole deploy before
// showcases were copied. og-image regeneration is cosmetic — the existing one is reused on failure.
console.log('\n🎨 Génération og-image.png...');
try {
execSync('npx tsx scripts/generate-og-image.ts', { cwd: ROOT, stdio: 'inherit' });
} catch (e) {
console.warn(` ⚠️ og-image generation failed (non-fatal, keeping existing): ${(e as Error).message.split('\n')[0]}`);
}
// Step 3 — Copy index.html + og-image.png
console.log('\n📋 Copie index.html + og-image.png → /opt/clone-architect-site/...');
const indexContent = readFileSync(PUBLIC_INDEX, 'utf-8');
writeWithSudo(join(DEPLOY_DIR, 'index.html'), indexContent);
const ogPng = join(ROOT, 'public', 'og-image.png');
if (existsSync(ogPng)) copyWithSudo(ogPng, join(DEPLOY_DIR, 'og-image.png'));
console.log(' ✅ index.html + og-image.png déployés');
// Step 4 — Copy showcase for each brand
console.log('\n🎨 Déploiement des showcases...');
const catalogRaw = readFileSync(CATALOG_PATH, 'utf-8');
const catalog = JSON.parse(catalogRaw);
let deployed = 0;
let skipped = 0;
for (const brand of catalog.brands) {
const domain = brand.domain;
const showcaseSrc = join(ROOT, 'extractions', domain, 'showcase', 'index.html');
const showcaseDest = join(DEPLOY_DIR, 'brands', domain, 'index.html');
if (existsSync(showcaseSrc)) {
copyWithSudo(showcaseSrc, showcaseDest);
deployed++;
} else {
skipped++;
}
}
console.log(` ✅ ${deployed} showcases déployés → /opt/clone-architect-site/brands/`);
if (skipped > 0) console.log(` ⏭ ${skipped} brands sans showcase (extraction incomplète)`);
// Step 4b — Generate sitemap.xml (SEO indexation)
console.log('\n🗺️ Génération sitemap.xml...');
const baseUrl = 'https://clone-architect.ps-tools.dev';
const today = new Date().toISOString().slice(0, 10);
const sitemapUrls = [
`