Compare commits
13 Commits
97c6462254
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
9752501ee5
|
|||
|
75f7f4b09c
|
|||
|
21b28ba376
|
|||
|
bbf1f0dde7
|
|||
|
7b63c0c922
|
|||
|
93bbfee7f7
|
|||
|
c81cc54d91
|
|||
|
88f0e84537
|
|||
|
502bc1765f
|
|||
|
4a79266a27
|
|||
|
27808cfd0e
|
|||
|
9a84e9003a
|
|||
|
56b2377cd1
|
27
.devcontainer/devcontainer.json
Normal file
27
.devcontainer/devcontainer.json
Normal file
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"name": "mail-landing",
|
||||
"image": "mcr.microsoft.com/devcontainers/javascript-node:1-22-bookworm",
|
||||
"onCreateCommand": "npm install -g pnpm@10",
|
||||
"postCreateCommand": "pnpm install",
|
||||
"forwardPorts": [3000],
|
||||
"portsAttributes": {
|
||||
"3000": {
|
||||
"label": "Preview",
|
||||
"onAutoForward": "notify"
|
||||
}
|
||||
},
|
||||
"customizations": {
|
||||
"vscode": {
|
||||
"extensions": [
|
||||
"esbenp.prettier-vscode",
|
||||
"stylelint.vscode-stylelint",
|
||||
"dbaeumer.vscode-eslint"
|
||||
],
|
||||
"settings": {
|
||||
"editor.formatOnSave": true,
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
}
|
||||
}
|
||||
},
|
||||
"remoteUser": "node"
|
||||
}
|
||||
@@ -1,9 +1,16 @@
|
||||
# Avoid sending secrets or dev tooling into the build context
|
||||
# config/ and plugins/ are included (no secrets; PHP configs read from ENV at runtime)
|
||||
.devcontainer
|
||||
.pnpm-store
|
||||
.vscode
|
||||
.woodpecker
|
||||
node_modules
|
||||
scripts
|
||||
.git
|
||||
.prettierrc
|
||||
.prettierignore
|
||||
*.md
|
||||
.env
|
||||
.env.*
|
||||
stylelint.config.js
|
||||
eslint.config.js
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,3 +1,5 @@
|
||||
.pnpm-store
|
||||
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
|
||||
@@ -1,2 +1,6 @@
|
||||
.pnpm-store
|
||||
|
||||
node_modules
|
||||
pnpm-lock.yaml
|
||||
|
||||
dist
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"semi": false,
|
||||
"singleQuote": true,
|
||||
"tabWidth": 2,
|
||||
"tabWidth": 4,
|
||||
"trailingComma": "none",
|
||||
"overrides": [
|
||||
{
|
||||
|
||||
@@ -48,7 +48,7 @@ steps:
|
||||
- pnpm install --frozen-lockfile
|
||||
- pnpm lint
|
||||
depends_on:
|
||||
- install
|
||||
- Prettier Format Check
|
||||
|
||||
- name: Send Lint Status Notification (failure)
|
||||
image: curlimages/curl
|
||||
@@ -76,7 +76,7 @@ steps:
|
||||
- pnpm install --frozen-lockfile
|
||||
- pnpm build
|
||||
depends_on:
|
||||
- install
|
||||
- Lint Check
|
||||
|
||||
- name: Send Build Status Notification (failure)
|
||||
image: curlimages/curl
|
||||
|
||||
@@ -12,7 +12,7 @@ when:
|
||||
evaluate: 'CI_PIPELINE_DEPLOY_TARGET == "production"'
|
||||
|
||||
depends_on:
|
||||
- ci
|
||||
- build
|
||||
|
||||
steps:
|
||||
- name: Trigger Portainer stack redeploy
|
||||
|
||||
@@ -81,7 +81,7 @@ Static landing site for **mifi.holdings** (and www). Plain HTML/CSS/JS source; a
|
||||
|
||||
- **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**, **critters** (build).
|
||||
- **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.
|
||||
|
||||
---
|
||||
|
||||
@@ -2,6 +2,7 @@ services:
|
||||
mifi-holdings-landing:
|
||||
image: git.mifi.dev/mifi-holdings/landing:latest
|
||||
container_name: mifi-holdings-landing
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- marina-net
|
||||
labels:
|
||||
@@ -9,7 +10,7 @@ services:
|
||||
- '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=security-supermax-with-analytics@file,redirect-www-to-non-www@file'
|
||||
- 'traefik.http.routers.holdings-landing.middlewares=gzip@file,security-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'
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"type": "module",
|
||||
"name": "mifi-holdings-landing",
|
||||
"type": "module",
|
||||
"version": "1.0.0",
|
||||
"packageManager": "pnpm@10.29.3",
|
||||
"scripts": {
|
||||
@@ -16,14 +16,17 @@
|
||||
"lint:fix": "pnpm run lint:fix:js && pnpm run lint:fix:css && pnpm run lint:fix:yaml",
|
||||
"lint:fix:js": "eslint src/ --fix",
|
||||
"lint:fix:css": "stylelint \"src/**/*.css\" --fix",
|
||||
"lint:fix:yaml": "yamllint .woodpecker/ci.yml .woodpecker/build.yml .woodpecker/deploy.yml docker-compose.yml --fix"
|
||||
"lint:fix:yaml": "yamllint .woodpecker/ci.yml .woodpecker/build.yml .woodpecker/deploy.yml docker-compose.yml --fix",
|
||||
"preview": "serve src -l 3000",
|
||||
"preview:prod": "pnpm build && serve dist -l 3000"
|
||||
},
|
||||
"devDependencies": {
|
||||
"clean-css": "^5.3.3",
|
||||
"critters": "^0.0.25",
|
||||
"beasties": "^0.4.1",
|
||||
"eslint": "^10.0.0",
|
||||
"eslint-config-prettier": "^10.1.8",
|
||||
"prettier": "^3.4.2",
|
||||
"serve": "^14.2.4",
|
||||
"stylelint": "^17.3.0",
|
||||
"stylelint-config-standard": "^40.0.0",
|
||||
"terser": "^5.46.0",
|
||||
|
||||
522
pnpm-lock.yaml
generated
522
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Build script: copy src → dist, minify JS/CSS, inline critical CSS (Critters).
|
||||
* Build script: copy src → dist, minify JS/CSS, inline critical CSS (Beasties).
|
||||
* Run with: pnpm build
|
||||
*/
|
||||
import {
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
} from 'fs'
|
||||
import { join, dirname, extname } from 'path'
|
||||
import { fileURLToPath } from 'url'
|
||||
import Critters from 'critters'
|
||||
import Beasties from 'beasties'
|
||||
import { minify as minifyJs } from 'terser'
|
||||
import CleanCSS from 'clean-css'
|
||||
|
||||
@@ -56,16 +56,18 @@ async function main() {
|
||||
if (!result.errors.length) writeFileSync(f, result.styles)
|
||||
}
|
||||
|
||||
// 4. Inline critical CSS with Critters (no browser; works in CI)
|
||||
const critters = new Critters({
|
||||
// 4. Inline critical CSS with Beasties for all HTML files (no browser; works in CI)
|
||||
const htmlFiles = distFiles.filter((f) => extname(f) === '.html')
|
||||
const beasties = new Beasties({
|
||||
path: distDir,
|
||||
preload: 'default',
|
||||
logLevel: 'warn'
|
||||
})
|
||||
const indexPath = join(distDir, 'index.html')
|
||||
const html = readFileSync(indexPath, 'utf8')
|
||||
const inlined = await critters.process(html)
|
||||
writeFileSync(indexPath, inlined)
|
||||
for (const htmlFile of htmlFiles) {
|
||||
const html = readFileSync(htmlFile, 'utf8')
|
||||
const inlined = await beasties.process(html)
|
||||
writeFileSync(htmlFile, inlined)
|
||||
}
|
||||
|
||||
console.log('Build complete: dist/')
|
||||
}
|
||||
|
||||
1
src/.well-known/appspecific/com.chrome.devtools.json
Normal file
1
src/.well-known/appspecific/com.chrome.devtools.json
Normal file
@@ -0,0 +1 @@
|
||||
{}
|
||||
@@ -1,3 +1,23 @@
|
||||
.skip-link {
|
||||
position: absolute;
|
||||
top: -100%;
|
||||
left: 0;
|
||||
z-index: 100;
|
||||
padding: 0.75rem 1.25rem;
|
||||
background: #70ffd7;
|
||||
color: #1b1e22;
|
||||
font-weight: 700;
|
||||
text-decoration: none;
|
||||
border-radius: 0 0 0.25rem;
|
||||
transition: top 0.2s;
|
||||
}
|
||||
|
||||
.skip-link:focus {
|
||||
top: 0;
|
||||
outline: 2px solid #70ffd7;
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
height: 100%;
|
||||
|
||||
@@ -11,6 +11,11 @@
|
||||
src="/assets/js/ga-init.js"
|
||||
data-ga-id="G-4000VNMXLK"
|
||||
></script>
|
||||
<script
|
||||
defer
|
||||
src="https://analytics.mifi.holdings/script.js"
|
||||
data-website-id="ce2a7f8a-95e9-4bc5-93cb-2e6075d836b8"
|
||||
></script>
|
||||
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
|
||||
@@ -26,26 +31,64 @@
|
||||
|
||||
<link rel="stylesheet" href="assets/css/style.css" />
|
||||
|
||||
<link rel="icon" type="image/svg+xml" href="/assets/images/favicon.svg" />
|
||||
<link rel="icon" type="image/x-icon" href="/assets/images/favicon.ico" />
|
||||
<link
|
||||
rel="icon"
|
||||
type="image/svg+xml"
|
||||
href="/assets/images/favicon.svg"
|
||||
/>
|
||||
<link
|
||||
rel="icon"
|
||||
type="image/x-icon"
|
||||
href="/assets/images/favicon.ico"
|
||||
/>
|
||||
<link
|
||||
rel="apple-touch-icon"
|
||||
sizes="180x180"
|
||||
href="/assets/images/apple-touch-icon.png"
|
||||
/>
|
||||
|
||||
<script type="application/ld+json">
|
||||
{
|
||||
"@context": "https://schema.org",
|
||||
"@type": "WebSite",
|
||||
"name": "mifi Holdings",
|
||||
"url": "https://mifi.holdings",
|
||||
"description": "This is just a landing page so something exists at the root domain of all the digital holdings of mifi.",
|
||||
"publisher": {
|
||||
"@type": "Organization",
|
||||
"name": "mifi Ventures",
|
||||
"url": "https://mifi.ventures",
|
||||
"email": "postmaster@mifi.holdings"
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<a
|
||||
href="#main-content"
|
||||
class="skip-link"
|
||||
data-umami-event="skip to main content"
|
||||
>Skip to main content</a
|
||||
>
|
||||
<main id="main-content" class="container" tabindex="-1">
|
||||
<div class="emoji">🛸</div>
|
||||
<h1>Nothing to See Here</h1>
|
||||
<p>
|
||||
You've stumbled onto <b>mifi.holdings</b> — the legendary vault of
|
||||
digital oddities, curios, and coffee-fueled experiments belonging to a
|
||||
possibly-human, definitely-mysterious entity named
|
||||
<b>mifi</b>.<br /><br />
|
||||
You've stumbled onto <b>mifi.holdings</b> — the legendary
|
||||
vault of digital oddities, curios, and coffee-fueled experiments
|
||||
belonging to a possibly-human, definitely-mysterious entity
|
||||
named <b>mifi</b>.<br /><br />
|
||||
There's nothing here for you.<br />
|
||||
Go on. Shoo. Scram. Or just keep wondering.
|
||||
</p>
|
||||
</div>
|
||||
</main>
|
||||
<img
|
||||
src="https://analytics.mifi.holdings/p/wQ9GYnLIg"
|
||||
alt=""
|
||||
width="1"
|
||||
height="1"
|
||||
role="presentation"
|
||||
loading="eager"
|
||||
/>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
4
src/robots.txt
Normal file
4
src/robots.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
User-agent: *
|
||||
Allow: /
|
||||
|
||||
Sitemap: https://mifi.holdings/sitemap.xml
|
||||
8
src/sitemap.xml
Normal file
8
src/sitemap.xml
Normal file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
||||
<url>
|
||||
<loc>https://mifi.holdings/</loc>
|
||||
<changefreq>monthly</changefreq>
|
||||
<priority>1.0</priority>
|
||||
</url>
|
||||
</urlset>
|
||||
Reference in New Issue
Block a user