diff --git a/src/servers/sidecar/headscale/invites.ts b/src/servers/sidecar/headscale/invites.ts index 4719974d..c3cb2b05 100644 --- a/src/servers/sidecar/headscale/invites.ts +++ b/src/servers/sidecar/headscale/invites.ts @@ -95,13 +95,25 @@ async function relay(res: Response | string, wrap: (body: Record { const input = parseCreate(await readJson(ctx.req)); if (input instanceof Response) return input; const res = await callCompanion(creds, { path: INVITES_PATH, method: 'POST', body: input }); - return relay(res, (body) => ({ available: true, invite: body.invite ?? body })); + return relay(res, (body) => { + const raw = body.invite ?? body; + const invite = raw && typeof raw === 'object' ? (raw as Record) : {}; + const { deepLink: _deepLink, ...rest } = invite; + return { available: true, invite: rest }; + }); } /** diff --git a/src/workspaces/officerdev/src/apps/Headscale/InvitesView.tsx b/src/workspaces/officerdev/src/apps/Headscale/InvitesView.tsx index 1bdf9e9a..811c13a6 100644 --- a/src/workspaces/officerdev/src/apps/Headscale/InvitesView.tsx +++ b/src/workspaces/officerdev/src/apps/Headscale/InvitesView.tsx @@ -80,13 +80,12 @@ type InviteLinkPanelProps = { invite: HeadscaleInviteCreated; onDismiss: () => v const InviteLinkPanel = ({ invite, onDismiss }: InviteLinkPanelProps) => { const [showQr, setShowQr] = useState(true); - // The link goes in `text`, not `url`: it is a custom scheme (`officer-offscale://join#…`) and several - // share-sheet implementations only accept http(s) in the url field, rejecting the whole call. As text it - // is passed through verbatim by every messenger. A cancelled sheet rejects too — nothing to report there, - // the link is still on screen. + // Plain https now — the link lands on a page the companion serves, which bounces into the app. It goes in + // `url` rather than `text` so share targets treat it as a link and preserve the fragment. A cancelled sheet + // rejects — nothing to report there, the link is still on screen. const share = () => { void navigator - .share?.({ title: 'Join the tailnet', text: `Tap to join as ${invite.user}: ${invite.url}` }) + .share?.({ title: 'Join the tailnet', text: `Tap to join as ${invite.user}`, url: invite.url }) .catch(() => {}); };