feat: redesign remote tunnel settings and named tunnel workflow (#546)
* feat: add Cloudflare Tunnel settings for desktop app Add a 'Remote Tunnel' section in Settings (desktop-only) that lets users start/stop a Cloudflare quick tunnel on demand, with auto-generated password protection and a QR code for easy mobile access. - Server: 4 new API endpoints (check/status/start/stop) reusing the existing cloudflare-tunnel module - UI: TunnelSettings component with full state machine (checking → idle/not-available → starting → active → stopping) - QR code rendered via the qrcode package for in-app display - Hidden from VS Code extension (desktop/web only) * fix: use ?token= instead of ?p= in tunnel password URLs REST API endpoints were building passwordUrl with ?p=<token> but SessionAuthGate reads the ?token= query param, causing QR code auto-login to fail — the password was never extracted from the URL. Standardize all three tunnel URL construction sites to use ?token= so scanning the QR code correctly pre-fills and submits the password. * feat: secure remote tunnel access with one-time connect links * feat: redesign remote tunnel settings and access flow * fix: cleaned up unused desktop close code path * feat: overhaul named tunnel setup and persistence flow * chore: align codemirror language dependency resolution --------- Co-authored-by: Brian-Hwang <brian.hwang@cornelisnetworks.com>
This commit is contained in:
committed by
GitHub
co-authored by
Brian-Hwang
parent
a505378d79
commit
d5d0d35083
@@ -20,7 +20,7 @@ import {
|
||||
RiCloudLine,
|
||||
RiFoldersLine,
|
||||
RiGitBranchLine,
|
||||
|
||||
RiGlobalLine,
|
||||
RiMicLine,
|
||||
RiNotification3Line,
|
||||
RiPaletteLine,
|
||||
@@ -97,6 +97,7 @@ const pageOrder: SettingsPageSlug[] = [
|
||||
'skills.installed',
|
||||
'skills.catalog',
|
||||
'voice',
|
||||
'tunnel',
|
||||
];
|
||||
|
||||
function buildRuntimeContext(isDesktop: boolean): SettingsRuntimeContext {
|
||||
@@ -150,6 +151,8 @@ function getSettingsNavIcon(slug: SettingsPageSlug): React.ComponentType<{ class
|
||||
return RiBarChart2Line;
|
||||
case 'voice':
|
||||
return RiMicLine;
|
||||
case 'tunnel':
|
||||
return RiGlobalLine;
|
||||
case 'home':
|
||||
return null;
|
||||
default:
|
||||
@@ -367,6 +370,7 @@ export const SettingsView: React.FC<SettingsViewProps> = ({ onClose, forceMobile
|
||||
sessions: 'sessions',
|
||||
notifications: 'notifications',
|
||||
voice: 'voice',
|
||||
tunnel: 'tunnel',
|
||||
}), []);
|
||||
|
||||
const renderUnavailable = React.useCallback(() => {
|
||||
@@ -437,7 +441,8 @@ export const SettingsView: React.FC<SettingsViewProps> = ({ onClose, forceMobile
|
||||
case 'shortcuts':
|
||||
case 'sessions':
|
||||
case 'notifications':
|
||||
case 'voice': {
|
||||
case 'voice':
|
||||
case 'tunnel': {
|
||||
const section = openChamberSectionBySlug[slug] ?? 'visual';
|
||||
return <OpenChamberPage section={section} />;
|
||||
}
|
||||
@@ -511,7 +516,7 @@ export const SettingsView: React.FC<SettingsViewProps> = ({ onClose, forceMobile
|
||||
)}
|
||||
>
|
||||
<span className="typography-ui-label font-normal truncate">{page.title}</span>
|
||||
{page.slug === 'voice' && (
|
||||
{(page.slug === 'voice' || page.slug === 'tunnel') && (
|
||||
<span className="shrink-0 typography-micro px-1 rounded leading-none pb-px text-[var(--status-warning)] bg-[var(--status-warning)]/10">
|
||||
beta
|
||||
</span>
|
||||
|
||||
Reference in New Issue
Block a user