From 91a45b4b6c7cd58f0d9e7ba4065804989fd869ef Mon Sep 17 00:00:00 2001 From: root Date: Tue, 9 Jun 2026 00:42:40 +1000 Subject: [PATCH] feat(apps): MagicMirror as a Void app (#/mirror, mirror.hynesy.com) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Embed MagicMirror² (CT 111) via the shared embedView factory, exposed at mirror.hynesy.com through Traefik + CF Access. Traefik mirror-frame middleware swaps MM's X-Frame-Options for a CSP frame-ancestors allowing the Void origins. Co-Authored-By: Claude Opus 4.8 --- CHANGELOG.md | 5 +++++ package.json | 2 +- public/app.js | 1 + public/components/sidebar.js | 3 ++- public/router.js | 1 + public/views/mirror.js | 6 ++++++ server.js | 2 +- 7 files changed, 17 insertions(+), 3 deletions(-) create mode 100644 public/views/mirror.js diff --git a/CHANGELOG.md b/CHANGELOG.md index 08cb783..4a68a86 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,11 @@ All notable changes to Void 2.0 are documented here. Format: [Keep a Changelog](https://keepachangelog.com). +## 2.3.0 — MagicMirror² as a Void app +- **New "MagicMirror" Apps view** (`#/mirror`, `public/views/mirror.js`) — embeds the smart-mirror dashboard (CT 111) via the shared `embedView` factory, like Timelapse / AI Usage. +- **Exposure:** MagicMirror (LAN-only `192.168.1.224:8080`) is now published at **mirror.hynesy.com** through Traefik + the `*.hynesy.com` tunnel, private behind **CF Access** (Farm policy / Google IdP). A Traefik `mirror-frame` middleware replaces MM's `X-Frame-Options: SAMEORIGIN` with a CSP `frame-ancestors` allowing the Void origins so the iframe renders. +- Unrelated to the Void code: CT 111 itself was updated **MagicMirror 2.25.0 → 2.36.0** on **Node 22**. + ## 2.2.0 — Links: self-hosted URL shortener (Kutt) as a Void app - **New "Links" Apps view** (`#/links`, `public/views/links.js`) — a Void-native card (Kutt **version / update tracker** + one-field **quick-add shortener**) on top of the blackflame-themed **Kutt** UI embedded via iframe (`link.hynesy.com`). Hybrid model: native convenience + the full Kutt UI in one tab. - **`/api/kutt` proxy** (`lib/api/routes/kutt.js`, `lib/links/kutt.js`) — owner-gated server-side proxy that holds the Kutt API key (`GET /version` vs latest GitHub release, cached 6h; `POST /` create; `GET /recent`). The key never reaches the browser. *(Mounted at `/api/kutt`, not `/api/links` — the latter is the Void's existing internal cross-entity linking router.)* diff --git a/package.json b/package.json index 5f64d62..c737075 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "void-server", - "version": "2.2.0", + "version": "2.3.0", "type": "module", "private": true, "scripts": { diff --git a/public/app.js b/public/app.js index 26f555f..1c3c788 100644 --- a/public/app.js +++ b/public/app.js @@ -29,6 +29,7 @@ const VIEWS = { 'ai-usage': () => import('./views/aiusage.js'), obd2: () => import('./views/obd2.js'), links: () => import('./views/links.js'), + mirror: () => import('./views/mirror.js'), settings: () => import('./views/settings.js'), jobs: () => import('./views/jobs.js') }; diff --git a/public/components/sidebar.js b/public/components/sidebar.js index 41d96e7..7caa2f9 100644 --- a/public/components/sidebar.js +++ b/public/components/sidebar.js @@ -133,7 +133,8 @@ export function renderSidebar(root) { navItem('Timelapse', '/timelapse'), navItem('AI Usage', '/ai-usage'), navItem('OBD2', '/obd2'), - navItem('Links', '/links') + navItem('Links', '/links'), + navItem('MagicMirror', '/mirror') ) ); diff --git a/public/router.js b/public/router.js index 028b455..61f7c14 100644 --- a/public/router.js +++ b/public/router.js @@ -30,6 +30,7 @@ const ROUTES = [ { name: 'ai-usage', re: /^\/ai-usage$/, keys: [] }, { name: 'obd2', re: /^\/obd2$/, keys: [] }, { name: 'links', re: /^\/links$/, keys: [] }, + { name: 'mirror', re: /^\/mirror$/, keys: [] }, { name: 'settings', re: /^\/settings$/, keys: [] }, { name: 'jobs', re: /^\/jobs$/, keys: [] }, { name: 'home', re: /^\/?$/, keys: [] } diff --git a/public/views/mirror.js b/public/views/mirror.js new file mode 100644 index 0000000..7427716 --- /dev/null +++ b/public/views/mirror.js @@ -0,0 +1,6 @@ +// public/views/mirror.js — #/mirror (MagicMirror² on CT 111) +import { embedView } from './embed.js'; +export const render = embedView({ + title: 'MagicMirror', sub: 'smart mirror dashboard', + src: 'https://mirror.hynesy.com/' +}); diff --git a/server.js b/server.js index c0a44ea..981a77a 100644 --- a/server.js +++ b/server.js @@ -14,7 +14,7 @@ import { mcpAuth } from './lib/api/middleware/mcp_auth.js'; import { handleMcp } from './lib/mcp/http.js'; import httpProxy from 'http-proxy'; -const VERSION = '2.2.0'; +const VERSION = '2.3.0'; // Proxy /terminal (+ its WebSocket) to ttyd on CT 300, so the embedded terminal // works whether the Void is reached via Traefik (void2-app.hynesy.com) OR the