embed friends' sites live in desktop windows; frame the skopec mirror since skopec.dev blocks framing
This commit is contained in:
parent
bfa0840b0e
commit
d5dfea060f
4 changed files with 30 additions and 1 deletions
|
|
@ -41,6 +41,10 @@ heatmap**, a gym SBD panel and links to friends' sites.
|
||||||
`ND_HTTPHEADERS_FRAMEOPTIONS=ALLOWALL` and Forgejo needs `[cors] X_FRAME_OPTIONS = unset`.
|
`ND_HTTPHEADERS_FRAMEOPTIONS=ALLOWALL` and Forgejo needs `[cors] X_FRAME_OPTIONS = unset`.
|
||||||
A spinner covers the frame while the site boots. Discord sends `DENY` and keeps a link card
|
A spinner covers the frame while the site boots. Discord sends `DENY` and keeps a link card
|
||||||
instead.
|
instead.
|
||||||
|
- **Friend sites in windows** — clicking a friend's desktop icon opens their site in a
|
||||||
|
window. `bukovinafilip.com` and `vlcak.me` send no framing headers, so they load live;
|
||||||
|
`skopec.dev` sends `X-Frame-Options: SAMEORIGIN`, so the window frames the bundled mirror
|
||||||
|
at `/skopec.html` and the `www` button opens the real site.
|
||||||
- **Minecraft-style SMP window** — the SMP shortcut opens a join menu modelled on
|
- **Minecraft-style SMP window** — the SMP shortcut opens a join menu modelled on
|
||||||
Minecraft's multiplayer screen, with live status, player count, MOTD and version pulled
|
Minecraft's multiplayer screen, with live status, player count, MOTD and version pulled
|
||||||
from mcstatus.io through the site's own ping API.
|
from mcstatus.io through the site's own ping API.
|
||||||
|
|
|
||||||
|
|
@ -295,6 +295,14 @@ list reordering, not free placement. Don't confuse the two.
|
||||||
`Content-Security-Policy: frame-ancestors`. Discord keeps a link card because
|
`Content-Security-Policy: frame-ancestors`. Discord keeps a link card because
|
||||||
it sends `DENY` and can't be configured. Always verify headers before enabling
|
it sends `DENY` and can't be configured. Always verify headers before enabling
|
||||||
`embed` on a new service.
|
`embed` on a new service.
|
||||||
|
- Friend shortcuts reuse the same embed path, driven by `data.ts`. `friend.iframe`
|
||||||
|
is the `embed` flag; `friend.url` is what the frame loads and `friend.realUrl`
|
||||||
|
is what the www button opens — `createWebsiteApp`'s `embedUrl` option lets the
|
||||||
|
two differ. `bukovinafilip.com` and `vlcak.me` send no framing headers, so they
|
||||||
|
frame the live site; `skopec.dev` sends `X-Frame-Options: SAMEORIGIN` on every
|
||||||
|
path, so `friends[0].url` points at the bundled `/public/skopec.html` mirror
|
||||||
|
and only the button is live. Making a blocking site load *live* would need a
|
||||||
|
header-stripping proxy (allowlisted) — not implemented.
|
||||||
- **Embedded iframes swallow the pointer.** While dragging/resizing a window,
|
- **Embedded iframes swallow the pointer.** While dragging/resizing a window,
|
||||||
the moment the cursor passes over a service `<iframe>` the site's own document
|
the moment the cursor passes over a service `<iframe>` the site's own document
|
||||||
receives the `pointermove`/`pointerup`, so a gesture that listens on `window`
|
receives the `pointermove`/`pointerup`, so a gesture that listens on `window`
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,8 @@ interface Shortcut {
|
||||||
image?: string;
|
image?: string;
|
||||||
/** Show the real site in an iframe. Only for sites that allow framing. */
|
/** Show the real site in an iframe. Only for sites that allow framing. */
|
||||||
embed?: boolean;
|
embed?: boolean;
|
||||||
|
/** Frame src when it differs from `url` (e.g. a local mirror of a blocking site). */
|
||||||
|
embedUrl?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IconEntry {
|
interface IconEntry {
|
||||||
|
|
@ -58,10 +60,18 @@ const SERVICE_SHORTCUTS: Shortcut[] = [
|
||||||
{ id: "svc-gitdxd", icon: "🐙", label: "GitDXD", url: "https://git.dvorakmortonxd.dev", color: "#6a4fb3", desc: "Best Git Hosting OAT", embed: true },
|
{ id: "svc-gitdxd", icon: "🐙", label: "GitDXD", url: "https://git.dvorakmortonxd.dev", color: "#6a4fb3", desc: "Best Git Hosting OAT", embed: true },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
// Friends' sites, opened in a live window when they allow framing. bukovinafilip
|
||||||
|
// and vlcak.me send no framing headers, so `url` (== `realUrl`) is framed
|
||||||
|
// directly. skopec.dev sends `X-Frame-Options: SAMEORIGIN` on every path, so we
|
||||||
|
// frame the same-origin mirror at `/skopec.html` instead; the www button still
|
||||||
|
// opens the live site. To embed a site that blocks framing live, it would need a
|
||||||
|
// header-stripping proxy.
|
||||||
const FRIEND_SHORTCUTS: Shortcut[] = friends.map((f, i) => ({
|
const FRIEND_SHORTCUTS: Shortcut[] = friends.map((f, i) => ({
|
||||||
id: `friend-${i}`,
|
id: `friend-${i}`,
|
||||||
label: f.name,
|
label: f.name,
|
||||||
url: f.realUrl || f.url,
|
url: f.realUrl || f.url,
|
||||||
|
embedUrl: f.url,
|
||||||
|
embed: f.iframe,
|
||||||
color: "#c0392b",
|
color: "#c0392b",
|
||||||
icon: "🫂",
|
icon: "🫂",
|
||||||
image: f.img,
|
image: f.img,
|
||||||
|
|
@ -214,6 +224,7 @@ export default function WindowsDesktop() {
|
||||||
icon: s.icon,
|
icon: s.icon,
|
||||||
image: s.image,
|
image: s.image,
|
||||||
embed: s.embed,
|
embed: s.embed,
|
||||||
|
embedUrl: s.embedUrl,
|
||||||
});
|
});
|
||||||
setDynamicApps((prev) => (prev[id] ? prev : { ...prev, [id]: app }));
|
setDynamicApps((prev) => (prev[id] ? prev : { ...prev, [id]: app }));
|
||||||
openAppDef(app);
|
openAppDef(app);
|
||||||
|
|
|
||||||
|
|
@ -851,6 +851,12 @@ export function createWebsiteApp(opts: {
|
||||||
* sites that do not send `X-Frame-Options` / a `frame-ancestors` CSP.
|
* sites that do not send `X-Frame-Options` / a `frame-ancestors` CSP.
|
||||||
*/
|
*/
|
||||||
embed?: boolean;
|
embed?: boolean;
|
||||||
|
/**
|
||||||
|
* URL to load inside the frame when it differs from `url` (which the www
|
||||||
|
* button opens). Used for sites that block framing: we frame a same-origin
|
||||||
|
* mirror instead, while the button still points at the live site.
|
||||||
|
*/
|
||||||
|
embedUrl?: string;
|
||||||
}): AppDef {
|
}): AppDef {
|
||||||
return {
|
return {
|
||||||
id: opts.id,
|
id: opts.id,
|
||||||
|
|
@ -861,7 +867,7 @@ export function createWebsiteApp(opts: {
|
||||||
height: opts.embed ? 640 : 440,
|
height: opts.embed ? 640 : 440,
|
||||||
url: opts.url,
|
url: opts.url,
|
||||||
content: opts.embed ? (
|
content: opts.embed ? (
|
||||||
<EmbeddedSite title={opts.title} url={opts.url} />
|
<EmbeddedSite title={opts.title} url={opts.embedUrl ?? opts.url} />
|
||||||
) : (
|
) : (
|
||||||
<WebsiteApp
|
<WebsiteApp
|
||||||
title={opts.title}
|
title={opts.title}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue