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
|
|
|
# humanAI — landing page
|
|
|
|
|
|
|
|
|
|
React + Vite static site, served by nginx in the `humanai-landing` Docker container,
|
|
|
|
|
behind Nginx Proxy Manager on the `nginxproxymanager_default` network.
|
|
|
|
|
|
|
|
|
|
## Development
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
npm install
|
|
|
|
|
npm run dev
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Deploy
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
./deploy.sh
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
The script builds images locally, pushes them to piha.local via the `piha` Docker context,
|
|
|
|
|
and runs smoke tests against the mailer endpoint.
|
|
|
|
|
|
|
|
|
|
## Mailer
|
|
|
|
|
|
|
|
|
|
The `mailer/` directory contains a standalone Express microservice that handles the contact
|
|
|
|
|
form. It runs as `humanai-mailer` on the same Docker network as NPM so it can be proxied
|
|
|
|
|
without exposing any port to the host.
|
|
|
|
|
|
|
|
|
|
### Manual steps required before first deploy
|
|
|
|
|
|
|
|
|
|
**(a) Create a Fastmail app-password and write credentials to `mailer.env` on the deploy machine:**
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
# mailer.env (never commit this file)
|
|
|
|
|
SMTP_USER=youruser@fastmail.com
|
|
|
|
|
SMTP_PASS=your-app-password
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
**(b) In Nginx Proxy Manager → Proxy Host for `gethumanai.pl` → Custom Locations:**
|
|
|
|
|
|
|
|
|
|
| Field | Value |
|
|
|
|
|
|---|---|
|
|
|
|
|
| location | `/api` |
|
|
|
|
|
| scheme | `http` |
|
|
|
|
|
| forward hostname | `humanai-mailer` |
|
|
|
|
|
| port | `3000` |
|
2026-07-31 17:01:30 +02:00
|
|
|
|
|
|
|
|
## Known issues
|
|
|
|
|
|
|
|
|
|
**Open Graph metadata is Polish on `/en` routes.** `index.html` is a single static
|
|
|
|
|
document served for every path (`try_files … /index.html`), so `og:title`,
|
|
|
|
|
`og:description` and `og:url` are baked in Polish. `App.tsx` rewrites
|
|
|
|
|
`document.title` and `meta[name="description"]` at runtime, which browsers and
|
|
|
|
|
JS-executing crawlers honour — but Open Graph scrapers (Facebook, LinkedIn,
|
|
|
|
|
Slack) read the raw HTML and will show Polish copy when an `/en` link is shared.
|
|
|
|
|
|
|
|
|
|
**`hreflang` alternates are the same on every page.** All routes advertise the
|
|
|
|
|
same `pl` / `en` / `x-default` triple pointing at the two homepages, so
|
|
|
|
|
`/privacy` claims the homepage as its alternate.
|
|
|
|
|
|
|
|
|
|
Both have one root cause and one fix: prerender a per-language `index.html`
|
|
|
|
|
instead of serving one document for every route.
|