mifi c71ec612bb
All checks were successful
ci/woodpecker/push/ci Pipeline was successful
ci/woodpecker/push/build Pipeline was successful
ci/woodpecker/push/deploy Pipeline was successful
Accessibility fixes
2026-02-16 12:57:44 -03:00
2026-02-13 18:09:10 -03:00
2026-02-13 18:09:10 -03:00
2026-02-13 18:12:11 -03:00
2026-02-16 12:57:44 -03:00
2026-02-16 12:57:44 -03:00
2026-02-13 18:09:10 -03:00
2026-02-13 18:09:10 -03:00
2026-02-13 18:09:10 -03:00
2026-02-16 12:57:44 -03:00
2026-02-13 18:09:10 -03:00
2026-02-13 18:09:10 -03:00
2026-02-16 12:57:44 -03:00
2026-02-13 18:09:10 -03:00
2026-02-13 18:09:10 -03:00
2026-02-13 18:19:29 -03:00
2026-02-16 12:57:44 -03:00

mail-landing

Static landing site for mail.mifi.holdings (HTML/CSS/JS). Source lives in src/, is built to dist/, and is served in production by nginx inside a Docker container, with Traefik as the reverse proxy.


Tech stack & frameworks

Layer Technology
Runtime Node 22 (dev/build only); production is static files only
Package manager pnpm 10.x
Source Plain HTML, CSS, JS — no framework (Vite/React/etc.)
Build Custom script (scripts/build.js): copy, minify, inline critical CSS
Minification Terser (JS), clean-css (CSS)
Critical CSS Beasties — inlines above-the-fold CSS into HTML (no browser/headless; works in CI)
Lint / format ESLint, Stylelint, Prettier, yamllint (for Woodpecker & compose)
Production server nginx (Alpine) in Docker
Reverse proxy Traefik (via docker-compose labels; TLS, gzip, security middlewares)
CI/CD Woodpecker CI (Gitea); images pushed to git.mifi.dev registry

Project structure

mail-landing/
├── src/                    # Source (edited by hand)
│   ├── index.html
│   ├── help/index.html
│   └── assets/
│       ├── css/site.css
│       ├── js/ga-init.js
│       └── images/favicon.svg
├── dist/                   # Build output (gitignored in practice; produced by pnpm build)
├── scripts/
│   └── build.js            # Build: copy → minify JS/CSS → Beasties inline critical CSS
├── nginx/conf.d/           # nginx config for the container
├── docker-compose.yml      # Service definition + Traefik labels for mail.mifi.holdings
├── Dockerfile              # nginx:alpine + config + dist/
├── .woodpecker/
│   ├── ci.yml              # Lint, format check, build (PR + push to main)
│   ├── build.yml           # Build site → Docker image → push to registry
│   └── deploy.yml          # Trigger Portainer webhook + Mattermost notifications
├── .devcontainer/          # Dev Container (Node 22 + pnpm) for Cursor/VS Code
└── package.json

Architecture (high level)

  1. Develop in src/ (HTML/CSS/JS). No bundler; structure mirrors output.
  2. Build (pnpm build): src/dist/ (copy, minify JS/CSS, inline critical CSS via Beasties).
  3. Docker image: Dockerfile copies nginx/conf.d/ and dist/ into nginx:alpine; no Node in the image.
  4. Run: Container serves /usr/share/nginx/html on port 80. Traefik (external) terminates TLS for mail.mifi.holdings, applies gzip and security middlewares, and routes to this service on marina-net.
  5. Deploy: Woodpecker runs cibuild (site + image + push) → deploy (Portainer webhook redeploy + Mattermost).

Local development (Dev Container)

  1. Open in Dev Container
    In Cursor/VS Code: Command PaletteDev Containers: Reopen in Container (or Clone Repository in Container Volume when opening the repo).
    First time: builds the container (Node 22 + pnpm), runs pnpm install.

  2. Preview the site
    In the container terminal:

    • Quick preview (serves src/ as-is, no build):
      pnpm preview
    • Production-like (build then serve dist/):
      pnpm preview:prod
      Port 3000 is forwarded; open http://localhost:3000 (or use the “Preview” port notification).
  3. Other commands

    • pnpm build — build src/dist/ (minify JS/CSS, inline critical CSS)
    • pnpm lint / pnpm format — lint and format
    • pnpm docker:build — build production image (for local testing; image: git.mifi.dev/mifi-holdings/mail-landing:latest)

Build pipeline (what pnpm build does)

  1. Clean & copydist/ is removed; src/ is copied recursively to dist/.
  2. Minify JS — All .js files in dist/ are minified with Terser (no comments).
  3. Minify CSS — All .css files are minified with clean-css (level 2).
  4. Inline critical CSS — Beasties runs on every .html file in dist/ (default preload behavior; no headless browser).

Output: dist/ ready to be served or copied into the Docker image.


Deployment (Woodpecker CI/CD)

Pipelines live under .woodpecker/. Execution order:

Pipeline When What
ci (ci.yml) Every PR and every push to main Install → Prettier check → Lint (JS, CSS, YAML) → Build. Mattermost notifications on failure/success.
build (build.yml) Push/tag/manual on main, or deployment event for production Depends on ci. Site build → Docker image build (linux/amd64, tagged with commit SHA + latest) → Push to git.mifi.dev/mifi-holdings/mail-landing. Mattermost on success/failure.
deploy (deploy.yml) Same as build (runs after build) skip_clone: true; triggers Portainer webhook to redeploy the stack. Mattermost deploy success/failure.

Secrets (in Woodpecker): portainer_webhook_url, mattermost_*, gitea_registry_username, gitea_package_token.

Production: Stack is defined in Portainer (using this repos docker-compose.yml). Redeploy pulls git.mifi.dev/mifi-holdings/mail-landing:latest and restarts the service. Traefik (on marina-net) routes mail.mifi.holdings to this container with TLS (e.g. Lets Encrypt) and middlewares (gzip, security).


Production runtime (Docker + Traefik)

  • Image: git.mifi.dev/mifi-holdings/mail-landing:latest (nginx:alpine + nginx/conf.d/ + dist/).
  • Compose: docker-compose.yml defines one service, marina-net, Traefik labels for Host(\mail.mifi.holdings`), TLS, gzip, and security middlewares; healthcheck via wgeton/`.
  • nginx: Serves /usr/share/nginx/html; HTML no-cache, JS/CSS long-lived cache; static assets and directory/index handling as in nginx/conf.d/default.conf.

Quick reference (pnpm scripts)

Command Description
pnpm build Build src/dist/ (minify + critical CSS)
pnpm preview Serve src/ on port 3000 (no build)
pnpm preview:prod Build then serve dist/ on 3000
pnpm lint Lint JS, CSS, and Woodpecker/compose YAML
pnpm lint:fix Auto-fix lint where supported
pnpm format Prettier write
pnpm format:check Prettier check only
pnpm docker:build Build Docker image (linux/amd64)
pnpm docker:push Push image to registry (manual)
Description
The landing and help pages for mail.mifi.holdings
https://mail.mifi.holdings
Readme 155 KiB
Languages
HTML 64.5%
CSS 20.4%
JavaScript 14.8%
Dockerfile 0.3%