diff --git a/README.md b/README.md index 2a67c9c..8d9cb45 100644 --- a/README.md +++ b/README.md @@ -1,72 +1,134 @@ # www.kenjim.com -Static website served via nginx Docker container on zet.home.arpa, proxied through nginx SSL termination at `https://www.kenjim.com`. +KenJim Technologies marketing site — React SPA with Express contact form backend, deployed on **zet.home.arpa** behind nginx SSL termination. -## Development Workflow +## Deployment Architecture -Edit on Mac → push to Gitea → pull on zet → site updates live. +```mermaid +flowchart TD + subgraph internet["Internet"] + browser(["Client browser"]) + godaddy["GoDaddy DNS\nwww.kenjim.com CNAME → lair.kenjim.com\nlair.kenjim.com A → WAN IP (dynamic)"] + le["Let's Encrypt CA"] + end -``` -Mac Gitea (git.kenjim.com) zet.home.arpa - │ git push │ │ - ├─────────────────────────────────►│ │ - │ │ git pull │ - │ ├───────────────────────────►│ - │ │ │ docker compose up -d + subgraph pfsense["pfSense · home gateway"] + ddns["DDNS client\nupdates lair.kenjim.com A record\nwhen WAN IP changes"] + nat["NAT / Port Forward\nWAN :80 / :443 → zet :80 / :443"] + unbound["Unbound DNS Resolver\nwww / git / kenji.kenjim.com\n→ 172.27.0.35 (split DNS — bypasses NAT)"] + end + + subgraph zet["zet.home.arpa · 172.27.0.35"] + subgraph ssl["SSL — acme.sh + Let's Encrypt"] + acme["acme.sh\nDNS-01 challenge via GoDaddy API"] + cert["/etc/nginx/ssl/kenjim.com/\nfullchain.pem · key.pem\nauto-renewed by cron"] + end + + subgraph nginx_host["nginx · host process"] + nginxsrv[":80 → redirect HTTPS\n:443 · SSL termination\nproxy_pass → localhost:8080"] + end + + subgraph docker["Docker · network_mode: host"] + fe["www-kenjim-frontend\nnginx :8080\nReact SPA (pre-built image)\n/api/* proxied → :3001"] + api["www-kenjim-api\nExpress.js :3001\nPOST /contact handler"] + end + + bridge["Proton Mail Bridge\n127.0.0.1:1025 SMTP relay"] + gitea[("Gitea · git.kenjim.com\nkenjim/www.kenjim.com")] + end + + protonmail["Proton Mail\ninfo@kenjim.com → kenji@kenjim.com"] + + browser -->|"DNS lookup"| godaddy + browser -->|"HTTPS :443"| nat + nat -->|"port forward"| nginxsrv + unbound -.->|"LAN clients resolve direct"| nginxsrv + + nginxsrv -->|"proxy_pass :8080"| fe + fe -->|"proxy /api/*"| api + api -->|"SMTP 127.0.0.1:1025"| bridge + bridge -->|"relay via Proton servers"| protonmail + + acme <-->|"set DNS TXT record\nfor domain validation"| godaddy + acme <-->|"certificate issuance"| le + acme -->|"writes cert"| cert + cert -->|"loaded by"| nginxsrv + + ddns -->|"keeps A record current"| godaddy + + gitea -->|"git pull · docker compose up --build"| fe + gitea -->|"git pull · docker compose up --build"| api ``` ## Structure ``` www.kenjim.com/ -├── docker-compose.yml # runs nginx:alpine on host port 8080 -└── html/ # web root — edit files here - └── index.html +├── docker-compose.yml # both services run network_mode: host +├── frontend/ +│ ├── Dockerfile # node:20 build → nginx:alpine serve +│ ├── nginx.conf # :8080, SPA fallback, /api/ proxy → :3001 +│ └── src/ # React source (Vite) +└── backend/ + ├── Dockerfile + └── server.js # Express, POST /contact → nodemailer ``` -All files under `html/` are served directly. No build step required for static content. +## Development Workflow + +Edit on Mac → push to Gitea → pull + rebuild on zet: + +``` +Mac Gitea (git.kenjim.com) zet.home.arpa + │ git push │ │ + ├──────────────────────────────►│ │ + │ │ git pull │ + │ ├─────────────────────────►│ + │ │ │ docker compose up --build -d +``` ## Running on zet ```bash cd ~/workspace/src/personal/www.kenjim.com -docker compose up -d # start -docker compose down # stop -docker compose logs -f # watch logs +docker compose up --build -d # build images and start +docker compose down # stop +docker compose logs -f # watch logs +docker logs www-kenjim-api # backend only +docker logs www-kenjim-frontend # frontend only ``` -## Deploying Changes +## Environment -On zet after pushing from Mac: +`.env` lives on zet only (not in git). Required vars: + +| Variable | Example | Notes | +|---|---|---| +| `SMTP_HOST` | `127.0.0.1` | Proton Bridge SMTP host | +| `SMTP_PORT` | `1025` | Proton Bridge SMTP port | +| `SMTP_SECURE` | `false` | Bridge uses STARTTLS, not SSL-on-connect | +| `SMTP_USER` | `kenji@kenjim.com` | Bridge auth credential | +| `SMTP_PASS` | `` | From Bridge SMTP settings | +| `FROM_EMAIL` | `info@kenjim.com` | Sender address shown in emails | +| `CONTACT_TO` | `info@kenjim.com` | Destination for contact form submissions | + +## SSL Certificate + +Issued by Let's Encrypt via DNS-01 challenge using the GoDaddy API. Managed by acme.sh on zet: ```bash -cd ~/workspace/src/personal/www.kenjim.com -git pull -# nginx:alpine picks up file changes immediately via volume mount — no restart needed +# manual renewal (cron handles this automatically) +~/.acme.sh/acme.sh --renew -d kenjim.com --wildcard --dns dns_gd ``` -If `docker-compose.yml` itself changes, recreate the container: - -```bash -docker compose up -d -``` - -## How It Connects to nginx - -The container listens on host port `8080`. The nginx reverse proxy on zet routes `https://www.kenjim.com` → `http://127.0.0.1:8080`. - -See `~/workspace/src/personal/appa-net/zet.home.arpa/nginx/kenjim.conf` for the nginx config. +Cert location: `/etc/nginx/ssl/kenjim.com/` ## Mac Setup (first time) ```bash -# Clone the repo on your Mac git clone http://kenjim@git.kenjim.com/kenjim/www.kenjim.com.git cd www.kenjim.com - -# Edit html/ files, then: -git add . -git commit -m "your message" -git push +# edit frontend/src, then: +git add . && git commit -m "..." && git push ```