Private
Public Access
1
0
Files
websitebox/websitebox-diagram (1).jsx
constantprojects a440026701 Initial commit: complete WebsiteBox project
Docker-based self-hosted WordPress deployment system with:
- Four-container stack (nginx, wordpress/php-fpm, mariadb, certbot)
- Automatic SSL via Let's Encrypt with self-signed fallback
- First-boot WordPress setup via WP-CLI (GeneratePress + child theme, plugins)
- Interactive setup wizard and one-line install script
- Backup, update, healthcheck, and SSL renewal scripts

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 15:24:23 -07:00

401 lines
19 KiB
JavaScript
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import React, { useState } from 'react';
const WebsiteBoxDiagram = () => {
const [activeView, setActiveView] = useState('flow');
const UserStep = ({ number, title, description }) => (
<div className="flex items-start gap-3 p-4 bg-blue-50 border-2 border-blue-300 rounded-lg">
<div className="flex-shrink-0 w-8 h-8 bg-blue-500 text-white rounded-full flex items-center justify-center font-bold text-sm">
{number}
</div>
<div className="flex-1">
<div className="font-semibold text-blue-900">{title}</div>
<div className="text-sm text-blue-700 mt-1">{description}</div>
</div>
</div>
);
const AutomatedStep = ({ title, items }) => (
<div className="p-4 bg-green-50 border-2 border-green-300 rounded-lg">
<div className="font-semibold text-green-900 flex items-center gap-2">
<span className="text-green-500"></span> {title}
</div>
<ul className="mt-2 space-y-1">
{items.map((item, i) => (
<li key={i} className="text-sm text-green-700 flex items-start gap-2">
<span className="text-green-400 mt-0.5"></span>
<span>{item}</span>
</li>
))}
</ul>
</div>
);
const Arrow = ({ label }) => (
<div className="flex flex-col items-center py-2">
{label && <span className="text-xs text-gray-500 mb-1">{label}</span>}
<div className="text-gray-400 text-xl"></div>
</div>
);
const FlowView = () => (
<div className="space-y-2">
{/* Phase 1: Setup */}
<div className="bg-gray-100 rounded-xl p-4">
<h3 className="text-sm font-bold text-gray-600 uppercase tracking-wide mb-4">Phase 1: Initial Setup</h3>
<UserStep
number="1"
title="Provision VPS"
description="Sign up for BuyVM/Vultr/etc, create Ubuntu VPS, get IP address and SSH access"
/>
<Arrow label="SSH into server" />
<UserStep
number="2"
title="Run Install Command"
description={<span><span className="font-mono text-xs bg-blue-100 px-1 py-0.5 rounded">curl -fsSL https://&lt;INSTALL_URL_TBD&gt;/install.sh | bash</span></span>}
/>
<Arrow />
<AutomatedStep
title="install.sh runs automatically"
items={[
"Detects OS (Ubuntu/Debian)",
"Installs Docker & Docker Compose if missing",
"Adds user to docker group (activates via sg docker — no logout needed)",
"Clones WebsiteBox repository",
"Launches setup wizard"
]}
/>
<Arrow />
<UserStep
number="3"
title="Answer Setup Wizard"
description="Enter domain, site title, admin username, email, set your password, configure age gate"
/>
<Arrow />
<AutomatedStep
title="setup.sh generates configuration"
items={[
"Generates secure database passwords",
"Creates .env file with all settings",
"Generates WordPress security salts",
"Creates websitebox-data/ directory structure with correct permissions",
"Saves credentials to .credentials (only if password was auto-generated)"
]}
/>
</div>
{/* Phase 2: DNS */}
<div className="bg-gray-100 rounded-xl p-4">
<h3 className="text-sm font-bold text-gray-600 uppercase tracking-wide mb-4">Phase 2: Domain Configuration</h3>
<UserStep
number="4"
title="Configure DNS"
description="Point domain's A record to VPS IP address at your domain registrar"
/>
<div className="flex items-center justify-center py-3">
<div className="bg-yellow-100 border border-yellow-300 rounded-lg px-4 py-2 text-sm text-yellow-800">
Wait for DNS propagation (usually 5-30 min, can take up to 48h)
</div>
</div>
</div>
{/* Phase 3: Launch */}
<div className="bg-gray-100 rounded-xl p-4">
<h3 className="text-sm font-bold text-gray-600 uppercase tracking-wide mb-4">Phase 3: Launch</h3>
<UserStep
number="5"
title="Start WebsiteBox"
description={<span><span className="font-mono text-xs bg-blue-100 px-1 py-0.5 rounded">docker compose up -d</span></span>}
/>
<Arrow />
<AutomatedStep
title="Containers start and self-configure"
items={[
"Pulls/builds container images",
"MariaDB initializes and passes healthcheck",
"WordPress entrypoint: waits for DB → installs core, themes, plugins via WP-CLI",
"nginx entrypoint: detects no SSL cert → serves HTTP → runs certbot → reloads with HTTPS",
"Age Gate configured, default content removed, permalinks set"
]}
/>
<div className="flex items-center justify-center py-3">
<div className="bg-amber-50 border border-amber-300 rounded-lg px-4 py-2 text-sm text-amber-800">
If SSL fails (DNS not ready yet), nginx serves an HTTP page explaining the issue.
<div className="text-xs mt-1">Fix: wait for DNS to propagate, then <span className="font-mono bg-amber-100 px-1 rounded">docker compose restart nginx</span></div>
</div>
</div>
<Arrow />
<UserStep
number="6"
title="Visit Your Site"
description="https://yourdomain.com shows your site with age gate. Log in at /wp-admin to start customizing."
/>
</div>
{/* Phase 4: Customize */}
<div className="bg-gray-100 rounded-xl p-4">
<h3 className="text-sm font-bold text-gray-600 uppercase tracking-wide mb-4">Phase 4: Customize & Use</h3>
<UserStep
number="7"
title="Customize Site"
description="Add content, upload images, customize theme — all via WordPress admin GUI"
/>
<div className="mt-4 p-3 bg-white rounded-lg border border-gray-200">
<div className="text-sm font-medium text-gray-700 mb-2">Ongoing automated maintenance:</div>
<div className="grid grid-cols-2 gap-2 text-xs">
<div className="bg-green-50 p-2 rounded text-green-700">🔄 SSL auto-renewal (12h check)</div>
<div className="bg-green-50 p-2 rounded text-green-700">💾 Daily DB + weekly full backups</div>
<div className="bg-green-50 p-2 rounded text-green-700">🔒 Wordfence monitoring</div>
<div className="bg-green-50 p-2 rounded text-green-700">🚀 Container auto-restart on reboot</div>
<div className="bg-green-50 p-2 rounded text-green-700">🔧 WordPress minor auto-updates</div>
<div className="bg-green-50 p-2 rounded text-green-700">🗑 Backup retention cleanup</div>
</div>
</div>
</div>
</div>
);
const ArchitectureView = () => (
<div className="space-y-6">
{/* Server diagram */}
<div className="bg-gray-800 rounded-xl p-6 text-white">
<h3 className="text-sm font-bold text-gray-400 uppercase tracking-wide mb-4">VPS Server Architecture</h3>
<div className="flex items-center justify-center mb-4">
<div className="text-center">
<div className="text-3xl mb-1">🌐</div>
<div className="text-sm text-gray-400">Internet</div>
</div>
</div>
<div className="flex justify-center mb-2">
<div className="w-0.5 h-6 bg-gray-600"></div>
</div>
<div className="text-center text-xs text-gray-500 mb-2">Ports 80, 443 only</div>
<div className="flex justify-center mb-2">
<div className="w-0.5 h-6 bg-gray-600"></div>
</div>
{/* Docker container stack */}
<div className="border-2 border-dashed border-gray-600 rounded-xl p-4">
<div className="text-xs text-gray-500 mb-1 text-center">Docker Compose Stack</div>
<div className="text-xs text-gray-600 mb-3 text-center italic">restart: unless-stopped on all containers</div>
{/* Nginx */}
<div className="bg-green-700 rounded-lg p-3 mb-3">
<div className="font-semibold flex items-center gap-2">
<span>📦</span> nginx (custom image w/ entrypoint)
</div>
<div className="text-xs text-green-200 mt-1">SSL termination Auto-acquires certs on first boot Security headers Static files</div>
<div className="text-xs text-green-300 mt-1 opacity-75">Healthcheck: curl localhost/nginx-health</div>
</div>
<div className="flex justify-center mb-2">
<div className="text-gray-500 text-sm"> FastCGI</div>
</div>
{/* WordPress */}
<div className="bg-blue-700 rounded-lg p-3 mb-3">
<div className="font-semibold flex items-center gap-2">
<span>📦</span> wordpress (custom image w/ WP-CLI)
</div>
<div className="text-xs text-blue-200 mt-1">PHP-FPM First-boot entrypoint installs themes, plugins, configures site via WP-CLI</div>
<div className="text-xs text-blue-300 mt-1 opacity-75">Healthcheck: php-fpm-healthcheck depends_on: db (healthy)</div>
</div>
<div className="flex justify-center mb-2">
<div className="text-gray-500 text-sm"> MySQL protocol</div>
</div>
{/* MariaDB */}
<div className="bg-orange-700 rounded-lg p-3 mb-3">
<div className="font-semibold flex items-center gap-2">
<span>📦</span> mariadb (database)
</div>
<div className="text-xs text-orange-200 mt-1">Internal only Not exposed to host</div>
<div className="text-xs text-orange-300 mt-1 opacity-75">Healthcheck: healthcheck --connect --innodb_initialized</div>
</div>
{/* Certbot */}
<div className="bg-purple-700 rounded-lg p-3">
<div className="font-semibold flex items-center gap-2">
<span>📦</span> certbot (SSL renewal)
</div>
<div className="text-xs text-purple-200 mt-1">Renewal loop every 12h Shares certbot-webroot volume with nginx</div>
</div>
</div>
{/* Volumes */}
<div className="mt-4">
<div className="text-center text-xs text-gray-400 mb-2">./websitebox-data/ single bind mount directory, all persistent data</div>
<div className="grid grid-cols-5 gap-2">
{['wordpress/', 'database/', 'certs/', 'certbot-webroot/', 'backups/'].map(name => (
<div key={name} className="bg-gray-700 rounded p-2 text-center text-xs">
<div className="text-yellow-400 mb-1">💾</div>
{name}
</div>
))}
</div>
</div>
</div>
{/* What's automated vs manual */}
<div className="grid grid-cols-2 gap-4">
<div className="bg-green-50 border-2 border-green-200 rounded-xl p-4">
<h4 className="font-bold text-green-800 mb-3 flex items-center gap-2">
<span></span> WebsiteBox Handles
</h4>
<ul className="space-y-2 text-sm text-green-700">
<li className="flex items-start gap-2"><span></span> Docker installation</li>
<li className="flex items-start gap-2"><span></span> Server configuration</li>
<li className="flex items-start gap-2"><span></span> SSL certificates (acquire + renew)</li>
<li className="flex items-start gap-2"><span></span> WordPress + theme + plugin install</li>
<li className="flex items-start gap-2"><span></span> Security hardening</li>
<li className="flex items-start gap-2"><span></span> Age gate setup</li>
<li className="flex items-start gap-2"><span></span> Automatic backups + retention</li>
<li className="flex items-start gap-2"><span></span> WordPress minor auto-updates</li>
<li className="flex items-start gap-2"><span></span> Container auto-restart on reboot</li>
</ul>
</div>
<div className="bg-blue-50 border-2 border-blue-200 rounded-xl p-4">
<h4 className="font-bold text-blue-800 mb-3 flex items-center gap-2">
<span>👤</span> User Does Manually
</h4>
<ul className="space-y-2 text-sm text-blue-700">
<li className="flex items-start gap-2"><span></span> Provision VPS ($3-6/mo)</li>
<li className="flex items-start gap-2"><span></span> Register domain ($10-15/yr)</li>
<li className="flex items-start gap-2"><span></span> Configure DNS A record</li>
<li className="flex items-start gap-2"><span></span> Run install command</li>
<li className="flex items-start gap-2"><span></span> Answer setup wizard</li>
<li className="flex items-start gap-2"><span></span> Create site content</li>
<li className="flex items-start gap-2"><span></span> Update plugins (via WP admin GUI)</li>
<li className="flex items-start gap-2"><span></span> Run update.sh (occasionally)</li>
<li className="flex items-start gap-2 text-blue-400"><span></span> Configure remote backups (optional)</li>
<li className="flex items-start gap-2 text-blue-400"><span></span> Set up SMTP (optional)</li>
</ul>
</div>
</div>
</div>
);
const FilesView = () => (
<div className="bg-gray-900 rounded-xl p-4 font-mono text-sm">
<div className="text-gray-400 mb-4"># Repository structure</div>
<div className="space-y-1 text-gray-300">
<div className="text-yellow-400">websitebox/</div>
<div className="pl-4"> <span className="text-green-400">docker-compose.yml</span></div>
<div className="pl-4"> <span className="text-green-400">.env.example</span></div>
<div className="pl-4"> <span className="text-cyan-400">install.sh</span> <span className="text-gray-500"> curl this to start</span></div>
<div className="pl-4"> <span className="text-cyan-400">setup.sh</span> <span className="text-gray-500"> interactive wizard</span></div>
<div className="pl-4"> README.md</div>
<div className="pl-4"></div>
<div className="pl-4"> <span className="text-gray-600">websitebox-data/</span> <span className="text-gray-500"> all persistent data (gitignored)</span></div>
<div className="pl-8 text-gray-500"> wordpress/ database/ certs/ certbot-webroot/ backups/</div>
<div className="pl-4"></div>
<div className="pl-4"> <span className="text-yellow-400">nginx/</span></div>
<div className="pl-8"> Dockerfile</div>
<div className="pl-8"> <span className="text-cyan-400">entrypoint.sh</span> <span className="text-gray-500"> SSL auto-bootstrap</span></div>
<div className="pl-8"> nginx.conf</div>
<div className="pl-8"> wordpress.conf</div>
<div className="pl-8"> wordpress-ssl.conf</div>
<div className="pl-8"> ssl-params.conf</div>
<div className="pl-4"></div>
<div className="pl-4"> <span className="text-yellow-400">wordpress/</span></div>
<div className="pl-8"> Dockerfile <span className="text-gray-500"> includes WP-CLI</span></div>
<div className="pl-8"> <span className="text-cyan-400">entrypoint.sh</span> <span className="text-gray-500"> first-boot WP-CLI setup</span></div>
<div className="pl-8"> wp-config-docker.php</div>
<div className="pl-8"> uploads.ini</div>
<div className="pl-8"> <span className="text-yellow-400">wp-content/</span></div>
<div className="pl-12"> <span className="text-yellow-400">themes/websitebox/</span> <span className="text-gray-500"> child theme</span></div>
<div className="pl-12"> <span className="text-yellow-400">mu-plugins/</span></div>
<div className="pl-16"> websitebox-setup.php <span className="text-gray-500"> status checker + XML-RPC disable</span></div>
<div className="pl-4"></div>
<div className="pl-4"> <span className="text-yellow-400">scripts/</span></div>
<div className="pl-8"> <span className="text-cyan-400">ssl-renew.sh</span></div>
<div className="pl-8"> <span className="text-cyan-400">backup.sh</span> <span className="text-gray-500"> manual backup + --prune-only</span></div>
<div className="pl-8"> <span className="text-cyan-400">update.sh</span> <span className="text-gray-500"> update WebsiteBox</span></div>
<div className="pl-8"> <span className="text-cyan-400">healthcheck.sh</span></div>
<div className="pl-4"></div>
<div className="pl-4"> <span className="text-yellow-400">docs/</span></div>
<div className="pl-8"> SECURITY.md</div>
<div className="pl-8"> TROUBLESHOOTING.md</div>
<div className="pl-8"> UPDATING.md</div>
</div>
</div>
);
return (
<div className="min-h-screen bg-gray-50 p-6">
<div className="max-w-3xl mx-auto">
{/* Header */}
<div className="text-center mb-6">
<h1 className="text-3xl font-bold text-gray-900 mb-2">📦 WebsiteBox</h1>
<p className="text-gray-600">Self-hosted WordPress portfolio in minutes</p>
</div>
{/* View tabs */}
<div className="flex gap-2 mb-6 bg-white rounded-lg p-1 shadow-sm">
{[
{ id: 'flow', label: 'User Flow' },
{ id: 'architecture', label: 'Architecture' },
{ id: 'files', label: 'File Structure' }
].map(tab => (
<button
key={tab.id}
onClick={() => setActiveView(tab.id)}
className={`flex-1 py-2 px-4 rounded-md text-sm font-medium transition-colors ${
activeView === tab.id
? 'bg-blue-500 text-white'
: 'text-gray-600 hover:bg-gray-100'
}`}
>
{tab.label}
</button>
))}
</div>
{/* Content */}
{activeView === 'flow' && <FlowView />}
{activeView === 'architecture' && <ArchitectureView />}
{activeView === 'files' && <FilesView />}
{/* Legend */}
<div className="mt-6 flex justify-center gap-6 text-sm">
<div className="flex items-center gap-2">
<div className="w-4 h-4 rounded bg-blue-300 border-2 border-blue-400"></div>
<span className="text-gray-600">User action required</span>
</div>
<div className="flex items-center gap-2">
<div className="w-4 h-4 rounded bg-green-300 border-2 border-green-400"></div>
<span className="text-gray-600">Automated by WebsiteBox</span>
</div>
</div>
</div>
</div>
);
};
export default WebsiteBoxDiagram;