humanai-web/deploy.sh
oskar 39d0662197 Add contact form mailer microservice and i18n support
- mailer/: standalone Express+nodemailer service on port 3000 with
  validation, honeypot (company field), per-IP rate limiting (5/min),
  Fastmail SMTP. Returns 400 on bad input, 429 on rate limit, 502 on
  SMTP failure, 200 {ok:true} on success or honeypot hit.
- Dockerfile: multi-stage nginx build for humanai-landing container.
- mailer/Dockerfile: node:22-alpine, no exposed host ports.
- deploy.sh: idempotent deploy of both containers to piha.local via
  Docker context 'piha', with smoke tests (400 on empty body, 200 on
  honeypot fill).
- src/i18n/: minimal pl/en translation files; useT() hook picks
  language from navigator.language.
- CTASection: replaced dead onSubmit with fetch POST /api/contact,
  added hidden honeypot field, success/error states using i18n strings.
- README: Mailer section with two manual steps (credentials + NPM proxy).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 15:12:05 +02:00

88 lines
3.1 KiB
Bash
Executable file

#!/usr/bin/env bash
set -euo pipefail
CONTEXT="piha"
REMOTE="ssh://oskar@piha.local"
NETWORK="nginxproxymanager_default"
# Create docker context if missing
docker context inspect "$CONTEXT" &>/dev/null || \
docker context create "$CONTEXT" --docker "host=$REMOTE"
D="docker --context $CONTEXT"
# ── Landing ────────────────────────────────────────────────────────────────────
echo "==> Building humanai-landing"
$D build -t humanai-landing .
echo "==> Deploying humanai-landing"
$D rm -f humanai-landing 2>/dev/null || true
$D run -d \
--name humanai-landing \
--network "$NETWORK" \
--restart unless-stopped \
humanai-landing
# ── Mailer ─────────────────────────────────────────────────────────────────────
echo "==> Building humanai-mailer"
$D build -t humanai-mailer ./mailer
echo "==> Deploying humanai-mailer"
$D rm -f humanai-mailer 2>/dev/null || true
$D run -d \
--name humanai-mailer \
--network "$NETWORK" \
--env-file mailer.env \
--restart unless-stopped \
humanai-mailer
# ── Smoke tests ────────────────────────────────────────────────────────────────
echo "==> Waiting for mailer to start..."
sleep 3
echo "==> Smoke test 1: empty body must return 400"
STATUS=$($D exec humanai-mailer node -e "
const http = require('http');
const body = '{}';
const req = http.request({
host: 'localhost', port: 3000, path: '/api/contact', method: 'POST',
headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(body) }
}, res => { console.log(res.statusCode); });
req.on('error', e => { console.error(e.message); process.exit(1); });
req.write(body);
req.end();
")
if [ "$STATUS" != "400" ]; then
echo "FAIL: expected 400, got $STATUS"
exit 1
fi
echo " OK: got 400"
echo "==> Smoke test 2: honeypot fill must return 200 {ok:true} (no email sent)"
RESP=$($D exec humanai-mailer node -e "
const http = require('http');
const body = JSON.stringify({name:'Bot',email:'bot@example.com',message:'hello world',company:'acme'});
const chunks = [];
const req = http.request({
host: 'localhost', port: 3000, path: '/api/contact', method: 'POST',
headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(body) }
}, res => {
res.on('data', d => chunks.push(d));
res.on('end', () => console.log(res.statusCode + ' ' + Buffer.concat(chunks).toString()));
});
req.on('error', e => { console.error(e.message); process.exit(1); });
req.write(body);
req.end();
")
if [[ "$RESP" != 200* ]]; then
echo "FAIL: expected 200, got $RESP"
exit 1
fi
echo " OK: got $RESP"
echo ""
echo "Deploy complete."
echo "Next steps (manual):"
echo " 1. Set SMTP_USER/SMTP_PASS in mailer.env on this machine"
echo " 2. In NPM: Proxy Host gethumanai.pl → Custom Location /api → http://humanai-mailer:3000"