From db75809bd0768d1ed9308ecc20e118836c181936 Mon Sep 17 00:00:00 2001 From: mifi Date: Fri, 13 Feb 2026 14:50:12 -0300 Subject: [PATCH] Tweaks and improvements --- .vscode/settings.json | 2 +- README.md | 144 ++++++++++++++++++++++++- docker-compose.yml | 48 ++++----- package.json | 9 +- src/assets/css/style.css | 62 +++++++++++ src/assets/images/apple-touch-icon.png | Bin 0 -> 3737 bytes src/assets/images/favicon.ico | Bin 0 -> 4286 bytes src/assets/images/favicon.svg | 9 ++ src/assets/js/ga-init.js | 11 ++ src/css/style.css | 59 ---------- src/index.html | 53 +++++---- 11 files changed, 284 insertions(+), 113 deletions(-) create mode 100644 src/assets/css/style.css create mode 100644 src/assets/images/apple-touch-icon.png create mode 100644 src/assets/images/favicon.ico create mode 100644 src/assets/images/favicon.svg create mode 100644 src/assets/js/ga-init.js delete mode 100644 src/css/style.css diff --git a/.vscode/settings.json b/.vscode/settings.json index 9e26dfe..0967ef4 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1 +1 @@ -{} \ No newline at end of file +{} diff --git a/README.md b/README.md index 604ebda..ed408ed 100644 --- a/README.md +++ b/README.md @@ -1 +1,143 @@ -# Simple Package (Docker) +# mifi.holdings — Landing Page + +Static landing site for **mifi.holdings** (and www). Plain HTML/CSS, no framework; 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 | + +--- + +## Architecture + +``` + ┌─────────────────┐ + │ Traefik │ (routing, TLS, websecure) + └────────┬────────┘ + │ + ┌────────▼────────┐ + │ Docker container │ mifi-holdings-landing + │ nginx:alpine │ port 80 + │ /usr/share/ │ + │ nginx/html ← │ static files from image + └─────────────────┘ +``` + +- **Build**: Docker image = `nginx:alpine` + project `nginx/conf.d/` + `src/` copied into `/usr/share/nginx/html`. +- **Run**: Single service on external network `marina-net`; Traefik routes `mifi.holdings` and `www.mifi.holdings` to this container (HTTPS, `security-prison@file` middleware). + +--- + +## Repo structure + +``` +. +├── src/ # Static site (served as-is) +│ ├── index.html +│ └── css/ +│ └── style.css +├── nginx/ +│ └── conf.d/ +│ └── default.conf # nginx server config (cache rules, SPA fallback) +├── .woodpecker/ +│ ├── ci.yml # Lint + format check (PR + push to main) +│ ├── build.yml # Build image, push to registry (main) +│ └── deploy.yml # Trigger Portainer redeploy (main) +├── docker-compose.yml # Service + Traefik labels for production +├── Dockerfile # nginx + config + src +├── package.json # pnpm scripts: format, lint, docker build/push +└── README.md +``` + +--- + +## Tech stack + +- **Frontend**: Static HTML + CSS only (no JS build step). +- **Server**: nginx (Alpine) in Docker. +- **Tooling**: **pnpm** (format with Prettier, lint with yamllint for `.woodpecker/*.yml` and `docker-compose.yml`). +- **Deployment**: Docker image → Gitea registry → Portainer stack redeploy. + +--- + +## Local development + +- **Dependencies**: `pnpm install` (only devDependencies: Prettier, yaml-lint). +- **Format**: `pnpm format` / `pnpm format:check`. +- **Lint**: `pnpm lint` (yamllint on Woodpecker and docker-compose YAML). +- **Run locally (Docker)**: + - Build: `pnpm docker:build` (or `docker build --platform linux/amd64 -t git.mifi.dev/mifi-holdings/landing:latest .`). + - Run: use `docker-compose up` **only** on a host that has `marina-net` and Traefik; otherwise run the image with a port map and open `http://localhost:`. + +There is no dev server in this repo; edit `src/` and refresh the browser or rebuild the image to test. + +--- + +## Docker + +- **Dockerfile**: Copies `nginx/conf.d/` and `src/` into an `nginx:alpine` image. No multi-stage build; the image is the final runtime. +- **Image**: Tagged as `git.mifi.dev/mifi-holdings/landing:latest` (and `:` in CI). +- **Local build/push**: `pnpm docker:build`, `pnpm docker:push` (requires login to `git.mifi.dev`). + +--- + +## CI/CD (Woodpecker) + +Three pipelines: + +1. **ci** (`.woodpecker/ci.yml`) + - **When**: Every PR and every push to `main`. + - **Steps**: Install deps → Prettier format check → yamllint (Woodpecker + docker-compose). Mattermost notifications on success/failure. + +2. **build** (`.woodpecker/build.yml`) + - **When**: Push/tag/manual on `main` (and deployment to production); **depends_on: ci**. + - **Steps**: Build Docker image (linux/amd64, up to 3 retries) → push `:latest` and `:` to `git.mifi.dev/mifi-holdings/landing`. Mattermost notifications. + +3. **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. +- **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 of `src/`). +- **HTML**: `Cache-Control: public, no-cache` so updates are visible quickly. +- **JS/CSS**: Long cache, `immutable` for hashed/versioned assets. +- **Images/fonts**: Cached (30d / 1y). +- **SPA-style fallback**: `/` tries `$uri`, `$uri/`, then `index.html`, then 404. + +--- + +## Summary + +- **What it is**: Static landing for mifi.holdings. +- **How it runs**: nginx in Docker, fronted by Traefik on `marina-net`. +- **How it’s updated**: Push to `main` → Woodpecker runs ci, build (image + push), deploy (Portainer webhook); Mattermost reports status. diff --git a/docker-compose.yml b/docker-compose.yml index b717932..3d0d9c3 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,34 +1,24 @@ services: - service: - image: git.mifi.dev/...:${IMAGE_TAG:-latest} - container_name: service - environment: - - ENV_NAME=value - healthcheck: - test: - [ - 'CMD', - '/usr/local/bin/healthcheck.sh', - '--connect', - '--innodb_initialized' - ] - retries: 10 - start_period: 20s + mifi-holdings-landing: + image: git.mifi.dev/mifi-holdings/landing:latest + container_name: mifi-holdings-landing networks: - - network - volumes: - - volume:/var/lib/... - - other_volume:/var/lib/... - depends_on: - - other service - restart: unless-stopped + - marina-net + labels: + - 'traefik.enable=true' + - 'traefik.docker.network=marina-net' + - 'traefik.http.routers.holdings-landing.rule=Host(`mifi.holdings`) || Host(`www.mifi.holdings`)' + - 'traefik.http.routers.holdings-landing.entrypoints=websecure' + - 'traefik.http.routers.holdings-landing.middlewares=ecurity-supermax-with-analytics@@file,redirect-www-to-non-www@file' + - 'traefik.http.routers.holdings-landing.tls=true' + - 'traefik.http.routers.holdings-landing.tls.certresolver=letsencrypt' + - 'traefik.http.services.holdings-landing.loadbalancer.server.port=80' + healthcheck: + test: ['CMD-SHELL', 'wget --spider -q http://localhost/ || exit 1'] + interval: 20s + timeout: 3s + retries: 3 networks: - network: + marina-net: external: true - -volumes: - volume: - external: true - other_volume: - external: false \ No newline at end of file diff --git a/package.json b/package.json index e12d6bc..ee188d5 100644 --- a/package.json +++ b/package.json @@ -1,14 +1,13 @@ { - "name": "...", + "name": "mifi-holdings-landing", "version": "1.0.0", - "private": true, "packageManager": "pnpm@10.29.3", "scripts": { "format": "prettier --write .", "format:check": "prettier --check .", "lint": "yamllint .woodpecker/ci.yml .woodpecker/build.yml .woodpecker/deploy.yml docker-compose.yml", - "docker:build": "docker build --platform linux/amd64 -t git.mifi.dev/.../...:latest .", - "docker:push": "docker push git.mifi.dev/.../...:latest" + "docker:build": "docker build --platform linux/amd64 -t git.mifi.dev/mifi-holdings/landing:latest .", + "docker:push": "docker push git.mifi.dev/mifi-holdings/landing:latest" }, "devDependencies": { "prettier": "^3.4.2", @@ -16,6 +15,6 @@ }, "repository": { "type": "git", - "url": "https://github.com/.../...git" + "url": "https://github.com/mifi-holdings/landing.git" } } diff --git a/src/assets/css/style.css b/src/assets/css/style.css new file mode 100644 index 0000000..64e8c73 --- /dev/null +++ b/src/assets/css/style.css @@ -0,0 +1,62 @@ +html, +body { + height: 100%; + margin: 0; + padding: 0; + background: #121212; + color: #f4f4f4; + font-family: 'Inter', Arial, sans-serif; +} +body { + display: flex; + align-items: center; + justify-content: center; + min-height: 100vh; +} +.container { + text-align: center; + background: rgba(34, 39, 44, 0.92); + padding: 3rem 2rem; + border-radius: 1.5rem; + box-shadow: 0 2px 24px 0 rgba(0, 0, 0, 0.13); + max-width: 400px; + margin: 1rem; +} +.emoji { + font-size: 2.8rem; + margin-bottom: 1rem; +} +h1 { + font-size: 2rem; + font-weight: 800; + margin-bottom: 0.5rem; + letter-spacing: -1px; +} +p { + color: #babed8; + margin-bottom: 1.7rem; + font-size: 1.14rem; + line-height: 1.55; +} +.scram { + display: inline-block; + padding: 0.7rem 1.4rem; + background: #70ffd7; + color: #1b1e22; + border-radius: 999px; + font-weight: 700; + font-size: 1.1rem; + text-decoration: none; + transition: background 0.2s; + margin-top: 0.5rem; + box-shadow: 0 1px 4px 0 rgba(112, 255, 215, 0.1); +} +.scram:hover { + background: #50bf9c; + color: #fff; +} +@media (max-width: 430px) { + .container { + padding: 2rem 0.6rem; + } +} diff --git a/src/assets/images/apple-touch-icon.png b/src/assets/images/apple-touch-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..b00ad45eacc7bb7dde4bb7b8a5b219c55a5725b4 GIT binary patch literal 3737 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 + + + \ No newline at end of file diff --git a/src/assets/js/ga-init.js b/src/assets/js/ga-init.js new file mode 100644 index 0000000..5c27172 --- /dev/null +++ b/src/assets/js/ga-init.js @@ -0,0 +1,11 @@ +(function () { + var script = document.currentScript; + var id = script && script.getAttribute('data-ga-id'); + if (!id) return; + window.dataLayer = window.dataLayer || []; + function gtag() { + window.dataLayer.push(arguments); + } + gtag('js', new Date()); + gtag('config', id, { anonymize_ip: true }); +})(); diff --git a/src/css/style.css b/src/css/style.css deleted file mode 100644 index 93e991b..0000000 --- a/src/css/style.css +++ /dev/null @@ -1,59 +0,0 @@ -html,body { - height: 100%; - margin: 0; - padding: 0; - background: #121212; - color: #f4f4f4; - font-family: 'Inter', Arial, sans-serif; -} -body { - display: flex; - align-items: center; - justify-content: center; - min-height: 100vh; -} -.container { - text-align: center; - background: rgba(34, 39, 44, 0.92); - padding: 3rem 2rem; - border-radius: 1.5rem; - box-shadow: 0 2px 24px 0 rgba(0,0,0,0.13); - max-width: 400px; - margin: 1rem; -} -.emoji { - font-size: 2.8rem; - margin-bottom: 1rem; -} -h1 { - font-size: 2rem; - font-weight: 800; - margin-bottom: 0.5rem; - letter-spacing: -1px; -} -p { - color: #babed8; - margin-bottom: 1.7rem; - font-size: 1.14rem; - line-height: 1.55; -} -.scram { - display: inline-block; - padding: 0.7rem 1.4rem; - background: #70ffd7; - color: #1b1e22; - border-radius: 999px; - font-weight: 700; - font-size: 1.1rem; - text-decoration: none; - transition: background 0.2s; - margin-top: 0.5rem; - box-shadow: 0 1px 4px 0 rgba(112,255,215,0.10); -} -.scram:hover { - background: #50bf9c; - color: #fff; -} -@media (max-width: 430px) { - .container { padding: 2rem 0.6rem; } -} diff --git a/src/index.html b/src/index.html index db1f569..b344ce2 100644 --- a/src/index.html +++ b/src/index.html @@ -1,20 +1,37 @@ - + - - - - mifi.holdings - - - -
-
🛸
-

Nothing to See Here

-

- You've stumbled onto mifi.holdings — the legendary vault of digital oddities, curios, and coffee-fueled experiments belonging to a possibly-human, definitely-mysterious entity named mifi.

- There's nothing here for you.
- Go on. Shoo. Scram. Or just keep wondering. -

-
- + + + + + + + + + mifi.holdings + + + + + + + + + + + + +
+
🛸
+

Nothing to See Here

+

+ You've stumbled onto mifi.holdings — the legendary vault of + digital oddities, curios, and coffee-fueled experiments belonging to a + possibly-human, definitely-mysterious entity named + mifi.

+ There's nothing here for you.
+ Go on. Shoo. Scram. Or just keep wondering. +

+
+