Some checks failed
ci/woodpecker/push/default Pipeline failed
Viktor asked to tighten who can see the immich-frame deployments: make them not public while keeping the two Meta Portals working as frames. The Portal app bakes the URL into the APK, so the same hostnames must keep loading from the home networks with zero device or router changes. - New shared Traefik middleware home-lans-only (Sofia/London/Valchedrym LANs + 10/8 + internal v6) — separate from local-only so the remote LANs don't inherit access to admin surfaces. - New ingress_factory dns_type="internal": publicly-resolvable A record carrying the internal Traefik LB IP (10.0.20.203). Outsiders resolve but can't route; WG spokes policy-route 10/8 down the tunnel. Never combine the allowlist with proxied DNS (cloudflared pod IPs are in 10/8 and would bypass it). - Both frame ingresses: dns_type internal + allowlist attached + external_monitor=false (drop the doomed [External] monitors). - rybbit worker: highlights-immich route/site removed (off Cloudflare). - Docs: CLAUDE.md/AGENTS.md ingress tiers, networking.md DNS categories, design doc docs/plans/2026-07-04-immich-frame-lan-only-design.md. Pre-verified: London router DNS returns RFC1918 answers unfiltered; Technitium already CNAMEs both hosts to the LB; no public wildcard. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
75 lines
2.6 KiB
JavaScript
75 lines
2.6 KiB
JavaScript
// Rybbit analytics injection via Cloudflare Worker
|
|
// Injects the rybbit tracking script into HTML responses using HTMLRewriter.
|
|
// Deployed as a route-based worker on *.viktorbarzin.me/*
|
|
|
|
// Site ID mapping: hostname → rybbit site ID
|
|
// These were previously injected via Traefik's rewrite-body plugin (broken on v3.6).
|
|
const SITE_IDS = {
|
|
"viktorbarzin.me": "da853a2438d0",
|
|
"www.viktorbarzin.me": "da853a2438d0",
|
|
"actualbudget.viktorbarzin.me": "3e6b6b68088a",
|
|
"crowdsec.viktorbarzin.me": "d09137795ccc",
|
|
"cyberchef.viktorbarzin.me": "7c460afc68c4",
|
|
"dawarich.viktorbarzin.me": "0abfd409f2fb",
|
|
"pma.viktorbarzin.me": "942c76b8bd4d",
|
|
"pgadmin.viktorbarzin.me": "7cef78e30485",
|
|
"audiobookshelf.viktorbarzin.me": "17a5c7fbb077",
|
|
"calibre.viktorbarzin.me": "ce5f8aed6bbb",
|
|
"stacks.viktorbarzin.me": "b38fda4285df",
|
|
"f1.viktorbarzin.me": "7e69786f66d5",
|
|
"frigate.viktorbarzin.me": "0d4044069ff5",
|
|
"immich.viktorbarzin.me": "35eedb7a3d2b",
|
|
"mail.viktorbarzin.me": "082f164faa7d",
|
|
"navidrome.viktorbarzin.me": "8a3844ff75ba",
|
|
"networking-toolbox.viktorbarzin.me": "50e38577e41c",
|
|
"nextcloud.viktorbarzin.me": "5a3bfe59a3fe",
|
|
"paperless-ngx.viktorbarzin.me": "be6d140cbed8",
|
|
"privatebin.viktorbarzin.me": "3ae810b0476d",
|
|
"wrongmove.viktorbarzin.me": "edee05de453d",
|
|
"rybbit.viktorbarzin.me": "3c476801a777",
|
|
"send.viktorbarzin.me": "c1b8f8aa831b",
|
|
"stirling-pdf.viktorbarzin.me": "a55ac54ec749",
|
|
"uptime-kuma.viktorbarzin.me": "8fef77b1f7fe",
|
|
"vaultwarden.viktorbarzin.me": "b8fc85e18683",
|
|
};
|
|
|
|
// Default site ID for any proxied host not in the map above.
|
|
// Set to null to skip injection for unmapped hosts.
|
|
const DEFAULT_SITE_ID = null;
|
|
|
|
class HeadInjector {
|
|
constructor(siteId) {
|
|
this.siteId = siteId;
|
|
}
|
|
|
|
element(element) {
|
|
element.prepend(
|
|
`<script src="https://rybbit.viktorbarzin.me/api/script.js" data-site-id="${this.siteId}" defer></script>`,
|
|
{ html: true }
|
|
);
|
|
}
|
|
}
|
|
|
|
export default {
|
|
async fetch(request) {
|
|
const url = new URL(request.url);
|
|
const hostname = url.hostname;
|
|
|
|
// Look up site ID for this hostname
|
|
const siteId = SITE_IDS[hostname] || DEFAULT_SITE_ID;
|
|
|
|
// Fetch the origin response
|
|
const response = await fetch(request);
|
|
|
|
// Only inject into HTML responses that have a site ID
|
|
const contentType = response.headers.get("content-type") || "";
|
|
if (!siteId || !contentType.includes("text/html")) {
|
|
return response;
|
|
}
|
|
|
|
// Use HTMLRewriter to inject the script before </head>
|
|
return new HTMLRewriter()
|
|
.on("head", new HeadInjector(siteId))
|
|
.transform(response);
|
|
},
|
|
};
|