93bbfee7f7b90a4c4e9d00409fdbe81d99385bb0
mifi.holdings — Landing Page
Static landing site for mifi.holdings (and www). Plain HTML/CSS/JS source; a build step produces minified assets and inlines critical CSS. Served by nginx in Docker behind Traefik, with CI/CD via Woodpecker and deployment via Portainer.
Quick reference
| What | Where |
|---|---|
| Site | mifi.holdings, www.mifi.holdings (HTTPS) |
| Runtime | nginx (Alpine) in Docker |
| Reverse proxy / TLS | Traefik (Let's Encrypt) |
| CI/CD | Woodpecker (ci → build → deploy) |
| Registry | git.mifi.dev/mifi-holdings/landing |
| Package manager | pnpm |
| Build output | dist/ (gitignored) |
Architecture
┌─────────────────┐
│ Traefik │ (routing, TLS, websecure)
└────────┬────────┘
│
┌────────▼────────┐
│ Docker container │ mifi-holdings-landing
│ nginx:alpine │ port 80
│ /usr/share/ │
│ nginx/html ← │ built output from dist/
└─────────────────┘
- Build:
pnpm buildcopiessrc/→dist/, minifies JS/CSS, inlines critical CSS (Critters). Docker image =nginx:alpine+nginx/conf.d/+dist/into/usr/share/nginx/html. - Run: Single service on external network
marina-net; Traefik routesmifi.holdingsandwww.mifi.holdingsto this container (HTTPS,security-prison@filemiddleware).
Repo structure
.
├── src/ # Source (HTML, CSS, JS)
│ ├── index.html
│ └── assets/
│ ├── css/style.css
│ ├── js/ga-init.js
│ └── images/ # favicon, etc.
├── scripts/
│ └── build.js # Build: copy, minify, inline critical CSS
├── dist/ # Build output (gitignored)
├── nginx/
│ └── conf.d/
│ └── default.conf # nginx server config (cache rules, SPA fallback)
├── .woodpecker/
│ ├── ci.yml # Lint, format, build check (PR + push to main)
│ ├── build.yml # Site build → Docker image → push (main)
│ └── deploy.yml # Trigger Portainer redeploy (main)
├── docker-compose.yml # Service + Traefik labels for production
├── Dockerfile # nginx + config + dist (not src)
├── package.json # pnpm scripts: build, format, lint, docker
└── README.md
Build (pnpm build)
- Output:
dist/(gitignored). Do not editdist/; it is generated. - Steps (see
scripts/build.js):- Clean and copy
src/→dist/. - Minify all
.js(terser) and.css(clean-css). - Inline critical CSS with Critters (lazy-loads the rest; no browser required, so it works in CI).
- Clean and copy
- When: Run before
docker build. CI and the build pipeline both runpnpm buildbefore packaging.
Tech stack
- Frontend: Static HTML + CSS + JS in
src/; production build minifies and inlines critical CSS. - Server: nginx (Alpine) in Docker.
- Tooling: pnpm; Prettier (format); ESLint (JS), Stylelint (CSS), yamllint (YAML); terser, clean-css, beasties (build).
- Deployment: Docker image (from
dist/) → Gitea registry → Portainer stack redeploy.
Local development
- Dependencies:
pnpm install. - Format:
pnpm format/pnpm format:check. - Lint:
pnpm lint(ESLint forsrc/**/*.js, Stylelint forsrc/**/*.css, yamllint for Woodpecker + docker-compose). Usepnpm lint:fixto auto-fix where supported. - Build:
pnpm build→ producesdist/. Required before building the Docker image. - Run locally (Docker):
- Build site:
pnpm build. - Image:
pnpm docker:build(ordocker build --platform linux/amd64 -t git.mifi.dev/mifi-holdings/landing:latest .). - Run: use
docker-compose uponly on a host that hasmarina-netand Traefik; otherwise run the image with a port map and openhttp://localhost:<port>.
- Build site:
No dev server; edit src/ and run pnpm build (and optionally docker run or a local static server on dist/) to test.
Docker
- Dockerfile: Copies
nginx/conf.d/anddist/(notsrc/) into annginx:alpineimage. Runpnpm buildfirst sodist/exists. - Image: Tagged as
git.mifi.dev/mifi-holdings/landing:latest(and:<commit-sha>in CI). - Local build/push:
pnpm build→pnpm docker:build→pnpm docker:push(requires login togit.mifi.dev).
CI/CD (Woodpecker)
Three pipelines:
-
ci (
.woodpecker/ci.yml)- When: Every PR and every push to
main. - Steps: Install deps → Prettier format check → lint (ESLint, Stylelint, yamllint) → Build (
pnpm build). Mattermost notifications on success/failure.
- When: Every PR and every push to
-
build (
.woodpecker/build.yml)- When: Push/tag/manual on
main(and deployment to production); depends_on: ci. - Steps: Site build (
pnpm install,pnpm build) → Docker image build (linux/amd64, up to 3 retries) → push:latestand:<CI_COMMIT_SHA>togit.mifi.dev/mifi-holdings/landing. Mattermost notifications.
- When: Push/tag/manual on
-
deploy (
.woodpecker/deploy.yml)- When: Same as build (main / production deploy); depends_on: ci.
- Steps: Call Portainer webhook to redeploy the stack. Mattermost notifications.
Secrets used: gitea_registry_username, gitea_package_token, portainer_webhook_url, mattermost_* (bot token, channel IDs, API URL).
Deployment (production)
- Orchestration: Stack defined in
docker-compose.yml, deployed via Portainer (webhook triggered by Woodpecker deploy pipeline). - Network: Container joins external network
marina-net(shared with Traefik). - Traefik:
- Hosts:
mifi.holdings,www.mifi.holdings. - Entrypoint:
websecure(HTTPS). - TLS: Let's Encrypt (
tls.certresolver=letsencrypt). - Middleware:
security-prison@file. - Backend: this service, port 80.
- Hosts:
- Healthcheck:
wget --spider -q http://localhost/every 20s (timeout 3s, 3 retries).
To deploy manually: pull the latest image and redeploy the stack in Portainer, or trigger the Portainer webhook (e.g. same URL as in portainer_webhook_url).
nginx behavior
- Root:
/usr/share/nginx/html(contents ofdist/after build). - HTML:
Cache-Control: public, no-cacheso updates are visible quickly. - JS/CSS: Long cache,
immutablefor hashed/versioned assets. - Images/fonts: Cached (30d / 1y).
- SPA-style fallback:
/tries$uri,$uri/, thenindex.html, then 404.
Summary
- What it is: Static landing for mifi.holdings; source in
src/, built output indist/. - How it runs: nginx in Docker serving
dist/, fronted by Traefik onmarina-net. - How it’s updated: Push to
main→ Woodpecker runs ci (lint, format, build), then build pipeline (site build → Docker image → push), then deploy (Portainer webhook); Mattermost reports status.
Description
The landing page for the (www.)mifi.holdings domain. Because the root of my digital empire needs a page.
https://mifi.holdings
Languages
JavaScript
40.6%
HTML
39%
CSS
19.3%
Dockerfile
1.1%