Migrate to Stack A: PHP-fpm + nginx + supervisord, drop flat HTML + Python API

- Remove old flat HTML pages (index, about, blog, contact, reviews, services/*, locations/*)
- Remove Python/Flask API container (api/)
- Remove old root nginx.conf and components/
- Add infra/: full nginx.conf (http block at /etc/nginx/nginx.conf), php-fpm-pool.conf (TCP listen), supervisord.conf, entrypoint.sh (auto-generates ALTCHA_HMAC_KEY)
- Add src/: PHP router, page/service/location/blog templates, contact handler, altcha handler, promo endpoint, SQLite data files
- Rewrite Dockerfile: single container, tini PID 1, healthcheck, all env vars declared
- Update docker-compose.yml: port 8096, env_file, healthcheck
- Update .dockerignore: exclude .env.*, include robots.txt/sitemap.xml/404.html/500.html
- Update assets: tokens.css, promo-popup.css/js, altcha.min.js, refactored form.js/main.js

Verified: all 17 routes 200, protection audit PASS, Resend confirmed working

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Concept Agent
2026-05-29 18:56:56 +02:00
parent 88ed4e6bda
commit 81feccdc1a
61 changed files with 2460 additions and 5747 deletions
+122
View File
@@ -0,0 +1,122 @@
<?php
declare(strict_types=1);
$db = new SQLite3(__DIR__ . '/../data/locations.sqlite', SQLITE3_OPEN_READONLY);
$db->busyTimeout(3000);
$loc = $db->querySingle("SELECT * FROM locations WHERE slug='" . SQLite3::escapeString($slug) . "'", true);
$db->close();
if (!$loc) {
http_response_code(404);
$page_title = '404: Location Not Found | Floor It Hardwood Floors';
$page_meta = '';
require __DIR__ . '/../components/_header.php';
echo '<section class="section section--light"><div class="container"><h1>Location Not Found</h1><p><a href="/locations/">View all locations</a></p></div></section>';
require __DIR__ . '/../components/_footer.php';
exit;
}
$page_title = $loc['title'];
$page_meta = $loc['meta_description'];
$body = json_decode($loc['body_json'] ?? '{}', true) ?? [];
$faqs = json_decode($loc['faqs_json'] ?? '[]', true) ?? [];
$city = $loc['city'];
require __DIR__ . '/../components/_header.php';
?>
<section class="page-hero section section--dark">
<div class="container">
<div class="page-hero-content">
<span class="eyebrow"><?= htmlspecialchars($loc['hero_eyebrow'] ?? '', ENT_QUOTES) ?></span>
<h1><?= htmlspecialchars($loc['hero_h1'] ?? '', ENT_QUOTES) ?></h1>
<p class="lead"><?= htmlspecialchars($loc['hero_lead'] ?? '', ENT_QUOTES) ?></p>
<a href="/contact/" class="btn btn--primary btn--lg">Request a <?= htmlspecialchars($city, ENT_QUOTES) ?> Estimate</a>
</div>
</div>
</section>
<?php if (!empty($body['overview_h2'])): ?>
<section class="section section--light">
<div class="container">
<div class="section-header">
<?php if (!empty($body['overview_eyebrow'])): ?><span class="eyebrow"><?= htmlspecialchars($body['overview_eyebrow'], ENT_QUOTES) ?></span><?php endif; ?>
<h2><?= htmlspecialchars($body['overview_h2'], ENT_QUOTES) ?></h2>
<div class="divider"></div>
<?php if (!empty($body['overview_body_1'])): ?><p><?= htmlspecialchars($body['overview_body_1'], ENT_QUOTES) ?></p><?php endif; ?>
<?php if (!empty($body['overview_body_2'])): ?><p style="margin-top:1rem;color:var(--smoke);"><?= htmlspecialchars($body['overview_body_2'], ENT_QUOTES) ?></p><?php endif; ?>
</div>
<?php
$stats = [];
for ($i = 1; $i <= 3; $i++) {
if (!empty($body["stat_{$i}_num"])) {
$stats[] = ['num' => $body["stat_{$i}_num"], 'label' => $body["stat_{$i}_label"] ?? '', 'sub' => $body["stat_{$i}_sub"] ?? ''];
}
}
if ($stats): ?>
<div class="stats-row">
<?php foreach ($stats as $stat): ?>
<div class="stat-item">
<strong><?= htmlspecialchars($stat['num'], ENT_QUOTES) ?></strong>
<span><?= htmlspecialchars($stat['label'], ENT_QUOTES) ?></span>
<?php if ($stat['sub']): ?><small><?= htmlspecialchars($stat['sub'], ENT_QUOTES) ?></small><?php endif; ?>
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>
</div>
</section>
<?php endif; ?>
<?php if (!empty($body['services_intro'])): ?>
<section class="section section--dark">
<div class="container">
<div class="section-header">
<span class="eyebrow">Services in <?= htmlspecialchars($city, ENT_QUOTES) ?></span>
<h2>Hardwood Floor Services in <?= htmlspecialchars($city, ENT_QUOTES) ?>, NY</h2>
<p class="lead"><?= htmlspecialchars($body['services_intro'], ENT_QUOTES) ?></p>
</div>
<div class="grid grid--3col services-local-grid">
<?php for ($i = 1; $i <= 3; $i++): $tk = "service_{$i}_title"; $bk = "service_{$i}_body"; ?>
<?php if (!empty($body[$tk])): ?>
<div class="card card--service-local">
<h3><?= htmlspecialchars($body[$tk], ENT_QUOTES) ?></h3>
<p><?= htmlspecialchars($body[$bk] ?? '', ENT_QUOTES) ?></p>
</div>
<?php endif; ?>
<?php endfor; ?>
</div>
</div>
</section>
<?php endif; ?>
<?php if ($faqs): ?>
<section class="section section--light">
<div class="container faq-section">
<div class="section-header">
<span class="eyebrow"><?= htmlspecialchars($city, ENT_QUOTES) ?> FAQs</span>
<h2>Common Questions from <?= htmlspecialchars($city, ENT_QUOTES) ?> Homeowners</h2>
</div>
<div class="faq-list">
<?php foreach ($faqs as $faq): ?>
<details class="faq-item">
<summary><?= htmlspecialchars($faq['q'] ?? '', ENT_QUOTES) ?></summary>
<p><?= htmlspecialchars($faq['a'] ?? '', ENT_QUOTES) ?></p>
</details>
<?php endforeach; ?>
</div>
</div>
</section>
<?php endif; ?>
<section class="section section--amber cta-section">
<div class="container cta-section-inner">
<div class="cta-section-text">
<h2><?= htmlspecialchars($body['form_h2'] ?? "Request a {$city} Floor Estimate", ENT_QUOTES) ?></h2>
<p>We respond to all <?= htmlspecialchars($city, ENT_QUOTES) ?> inquiries within 24 hours.</p>
</div>
<a href="/contact/" class="btn btn--primary btn--lg"><?= htmlspecialchars($body['form_submit'] ?? 'Send Estimate Request', ENT_QUOTES) ?></a>
</div>
</section>
<?php require __DIR__ . '/../components/_footer.php'; ?>