mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-02-04 09:13:08 +00:00
Merge pull request #471 from AutoMaker-Org/fix/dev-server-url
fix: use browser hostname for dev server URLs instead of localhost
This commit is contained in:
@@ -70,6 +70,8 @@ const eslintConfig = defineConfig([
|
|||||||
AbortSignal: 'readonly',
|
AbortSignal: 'readonly',
|
||||||
Audio: 'readonly',
|
Audio: 'readonly',
|
||||||
ScrollBehavior: 'readonly',
|
ScrollBehavior: 'readonly',
|
||||||
|
URL: 'readonly',
|
||||||
|
URLSearchParams: 'readonly',
|
||||||
// Timers
|
// Timers
|
||||||
setTimeout: 'readonly',
|
setTimeout: 'readonly',
|
||||||
setInterval: 'readonly',
|
setInterval: 'readonly',
|
||||||
|
|||||||
@@ -117,7 +117,7 @@ export function CreatePRDialog({
|
|||||||
description: `PR already exists for ${result.result.branch}`,
|
description: `PR already exists for ${result.result.branch}`,
|
||||||
action: {
|
action: {
|
||||||
label: 'View PR',
|
label: 'View PR',
|
||||||
onClick: () => window.open(result.result!.prUrl!, '_blank'),
|
onClick: () => window.open(result.result!.prUrl!, '_blank', 'noopener,noreferrer'),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
@@ -125,7 +125,7 @@ export function CreatePRDialog({
|
|||||||
description: `PR created from ${result.result.branch}`,
|
description: `PR created from ${result.result.branch}`,
|
||||||
action: {
|
action: {
|
||||||
label: 'View PR',
|
label: 'View PR',
|
||||||
onClick: () => window.open(result.result!.prUrl!, '_blank'),
|
onClick: () => window.open(result.result!.prUrl!, '_blank', 'noopener,noreferrer'),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -251,7 +251,10 @@ export function CreatePRDialog({
|
|||||||
<p className="text-sm text-muted-foreground mt-1">Your PR is ready for review</p>
|
<p className="text-sm text-muted-foreground mt-1">Your PR is ready for review</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex gap-2 justify-center">
|
<div className="flex gap-2 justify-center">
|
||||||
<Button onClick={() => window.open(prUrl, '_blank')} className="gap-2">
|
<Button
|
||||||
|
onClick={() => window.open(prUrl, '_blank', 'noopener,noreferrer')}
|
||||||
|
className="gap-2"
|
||||||
|
>
|
||||||
<ExternalLink className="w-4 h-4" />
|
<ExternalLink className="w-4 h-4" />
|
||||||
View Pull Request
|
View Pull Request
|
||||||
</Button>
|
</Button>
|
||||||
@@ -277,7 +280,7 @@ export function CreatePRDialog({
|
|||||||
<Button
|
<Button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (browserUrl) {
|
if (browserUrl) {
|
||||||
window.open(browserUrl, '_blank');
|
window.open(browserUrl, '_blank', 'noopener,noreferrer');
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
className="gap-2 w-full"
|
className="gap-2 w-full"
|
||||||
|
|||||||
@@ -143,8 +143,12 @@ export function WorktreeActionsDropdown({
|
|||||||
<span className="w-2 h-2 rounded-full bg-green-500 animate-pulse" />
|
<span className="w-2 h-2 rounded-full bg-green-500 animate-pulse" />
|
||||||
Dev Server Running (:{devServerInfo?.port})
|
Dev Server Running (:{devServerInfo?.port})
|
||||||
</DropdownMenuLabel>
|
</DropdownMenuLabel>
|
||||||
<DropdownMenuItem onClick={() => onOpenDevServerUrl(worktree)} className="text-xs">
|
<DropdownMenuItem
|
||||||
<Globe className="w-3.5 h-3.5 mr-2" />
|
onClick={() => onOpenDevServerUrl(worktree)}
|
||||||
|
className="text-xs"
|
||||||
|
aria-label={`Open dev server on port ${devServerInfo?.port} in browser`}
|
||||||
|
>
|
||||||
|
<Globe className="w-3.5 h-3.5 mr-2" aria-hidden="true" />
|
||||||
Open in Browser
|
Open in Browser
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuItem
|
<DropdownMenuItem
|
||||||
@@ -320,7 +324,7 @@ export function WorktreeActionsDropdown({
|
|||||||
<>
|
<>
|
||||||
<DropdownMenuItem
|
<DropdownMenuItem
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
window.open(worktree.pr!.url, '_blank');
|
window.open(worktree.pr!.url, '_blank', 'noopener,noreferrer');
|
||||||
}}
|
}}
|
||||||
className="text-xs"
|
className="text-xs"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -298,20 +298,29 @@ export function WorktreeTab({
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{isDevServerRunning && (
|
{isDevServerRunning && (
|
||||||
<Button
|
<TooltipProvider>
|
||||||
variant={isSelected ? 'default' : 'outline'}
|
<Tooltip>
|
||||||
size="sm"
|
<TooltipTrigger asChild>
|
||||||
className={cn(
|
<Button
|
||||||
'h-7 w-7 p-0 rounded-none border-r-0',
|
variant={isSelected ? 'default' : 'outline'}
|
||||||
isSelected && 'bg-primary text-primary-foreground',
|
size="sm"
|
||||||
!isSelected && 'bg-secondary/50 hover:bg-secondary',
|
className={cn(
|
||||||
'text-green-500'
|
'h-7 w-7 p-0 rounded-none border-r-0',
|
||||||
)}
|
isSelected && 'bg-primary text-primary-foreground',
|
||||||
onClick={() => onOpenDevServerUrl(worktree)}
|
!isSelected && 'bg-secondary/50 hover:bg-secondary',
|
||||||
title={`Open dev server (port ${devServerInfo?.port})`}
|
'text-green-500'
|
||||||
>
|
)}
|
||||||
<Globe className="w-3 h-3" />
|
onClick={() => onOpenDevServerUrl(worktree)}
|
||||||
</Button>
|
aria-label={`Open dev server on port ${devServerInfo?.port} in browser`}
|
||||||
|
>
|
||||||
|
<Globe className="w-3 h-3" aria-hidden="true" />
|
||||||
|
</Button>
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent>
|
||||||
|
<p>Open dev server (:{devServerInfo?.port})</p>
|
||||||
|
</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
|
</TooltipProvider>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<WorktreeActionsDropdown
|
<WorktreeActionsDropdown
|
||||||
|
|||||||
@@ -118,8 +118,37 @@ export function useDevServers({ projectPath }: UseDevServersOptions) {
|
|||||||
const handleOpenDevServerUrl = useCallback(
|
const handleOpenDevServerUrl = useCallback(
|
||||||
(worktree: WorktreeInfo) => {
|
(worktree: WorktreeInfo) => {
|
||||||
const serverInfo = runningDevServers.get(getWorktreeKey(worktree));
|
const serverInfo = runningDevServers.get(getWorktreeKey(worktree));
|
||||||
if (serverInfo) {
|
if (!serverInfo) {
|
||||||
window.open(serverInfo.url, '_blank');
|
logger.warn('No dev server info found for worktree:', getWorktreeKey(worktree));
|
||||||
|
toast.error('Dev server not found', {
|
||||||
|
description: 'The dev server may have stopped. Try starting it again.',
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Rewrite URL hostname to match the current browser's hostname.
|
||||||
|
// This ensures dev server URLs work when accessing Automaker from
|
||||||
|
// remote machines (e.g., 192.168.x.x or hostname.local instead of localhost).
|
||||||
|
const devServerUrl = new URL(serverInfo.url);
|
||||||
|
|
||||||
|
// Security: Only allow http/https protocols to prevent potential attacks
|
||||||
|
// via data:, javascript:, file:, or other dangerous URL schemes
|
||||||
|
if (devServerUrl.protocol !== 'http:' && devServerUrl.protocol !== 'https:') {
|
||||||
|
logger.error('Invalid dev server URL protocol:', devServerUrl.protocol);
|
||||||
|
toast.error('Invalid dev server URL', {
|
||||||
|
description: 'The server returned an unsupported URL protocol.',
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
devServerUrl.hostname = window.location.hostname;
|
||||||
|
window.open(devServerUrl.toString(), '_blank', 'noopener,noreferrer');
|
||||||
|
} catch (error) {
|
||||||
|
logger.error('Failed to parse dev server URL:', error);
|
||||||
|
toast.error('Failed to open dev server', {
|
||||||
|
description: 'The server URL could not be processed. Please try again.',
|
||||||
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[runningDevServers, getWorktreeKey]
|
[runningDevServers, getWorktreeKey]
|
||||||
|
|||||||
Reference in New Issue
Block a user