Skip to content

Commit aa9e155

Browse files
feat: auto-select single server and remove local execution (#102)
* feat: auto-select single server and remove local execution - Remove local execution option entirely, all scripts now execute via SSH - Auto-select and skip modal when exactly one server is configured - Add server settings button when no servers are configured - Auto-refresh server list when settings modal closes - Update modal title from 'Execution Mode' to 'Select Server' * fix: remove debug messages from WebSocket output - Remove console.log for WebSocket messages in Terminal component - Remove debug output from SSH command execution in installedScripts router - Clean up command output chunk logging and config data logging - Remove container check result debug messages - This eliminates unwanted debug messages appearing in terminal output
1 parent d819cd7 commit aa9e155

File tree

3 files changed

+92
-117
lines changed

3 files changed

+92
-117
lines changed

src/app/_components/ExecutionModeModal.tsx

Lines changed: 92 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import { useState, useEffect } from 'react';
44
import type { Server } from '../../types/server';
55
import { Button } from './ui/button';
6+
import { SettingsModal } from './SettingsModal';
67

78
interface ExecutionModeModalProps {
89
isOpen: boolean;
@@ -15,15 +16,33 @@ export function ExecutionModeModal({ isOpen, onClose, onExecute, scriptName }: E
1516
const [servers, setServers] = useState<Server[]>([]);
1617
const [loading, setLoading] = useState(false);
1718
const [error, setError] = useState<string | null>(null);
18-
const [selectedMode, setSelectedMode] = useState<'local' | 'ssh'>('local');
1919
const [selectedServer, setSelectedServer] = useState<Server | null>(null);
20+
const [hasAutoExecuted, setHasAutoExecuted] = useState(false);
21+
const [settingsModalOpen, setSettingsModalOpen] = useState(false);
2022

2123
useEffect(() => {
2224
if (isOpen) {
25+
setHasAutoExecuted(false);
2326
void fetchServers();
2427
}
2528
}, [isOpen]);
2629

30+
// Auto-execute when exactly one server is available
31+
useEffect(() => {
32+
if (isOpen && !loading && servers.length === 1 && !hasAutoExecuted) {
33+
setHasAutoExecuted(true);
34+
onExecute('ssh', servers[0]);
35+
onClose();
36+
}
37+
}, [isOpen, loading, servers, hasAutoExecuted, onExecute, onClose]);
38+
39+
// Refresh servers when settings modal closes
40+
const handleSettingsModalClose = () => {
41+
setSettingsModalOpen(false);
42+
// Refetch servers to reflect any changes made in settings
43+
void fetchServers();
44+
};
45+
2746
const fetchServers = async () => {
2847
setLoading(true);
2948
setError(null);
@@ -42,110 +61,60 @@ export function ExecutionModeModal({ isOpen, onClose, onExecute, scriptName }: E
4261
};
4362

4463
const handleExecute = () => {
45-
if (selectedMode === 'ssh' && !selectedServer) {
64+
if (!selectedServer) {
4665
setError('Please select a server for SSH execution');
4766
return;
4867
}
4968

50-
onExecute(selectedMode, selectedServer ?? undefined);
69+
onExecute('ssh', selectedServer);
5170
onClose();
5271
};
5372

54-
const handleModeChange = (mode: 'local' | 'ssh') => {
55-
setSelectedMode(mode);
56-
if (mode === 'local') {
57-
setSelectedServer(null);
58-
}
59-
};
60-
6173
if (!isOpen) return null;
6274

6375
return (
64-
<div className="fixed inset-0 backdrop-blur-sm bg-black/50 flex items-center justify-center z-50 p-4">
65-
<div className="bg-card rounded-lg shadow-xl max-w-md w-full border border-border">
66-
{/* Header */}
67-
<div className="flex items-center justify-between p-6 border-b border-border">
68-
<h2 className="text-xl font-bold text-foreground">Execution Mode</h2>
69-
<Button
70-
onClick={onClose}
71-
variant="ghost"
72-
size="icon"
73-
className="text-muted-foreground hover:text-foreground"
74-
>
75-
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
76-
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
77-
</svg>
78-
</Button>
79-
</div>
80-
81-
{/* Content */}
82-
<div className="p-6">
83-
<div className="mb-6">
84-
<h3 className="text-lg font-medium text-foreground mb-2">
85-
Where would you like to execute &quot;{scriptName}&quot;?
86-
</h3>
87-
76+
<>
77+
<div className="fixed inset-0 backdrop-blur-sm bg-black/50 flex items-center justify-center z-50 p-4">
78+
<div className="bg-card rounded-lg shadow-xl max-w-md w-full border border-border">
79+
{/* Header */}
80+
<div className="flex items-center justify-between p-6 border-b border-border">
81+
<h2 className="text-xl font-bold text-foreground">Select Server</h2>
82+
<Button
83+
onClick={onClose}
84+
variant="ghost"
85+
size="icon"
86+
className="text-muted-foreground hover:text-foreground"
87+
>
88+
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
89+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
90+
</svg>
91+
</Button>
8892
</div>
8993

90-
{error && (
91-
<div className="mb-4 p-3 bg-destructive/10 border border-destructive/20 rounded-md">
92-
<div className="flex">
93-
<div className="flex-shrink-0">
94-
<svg className="h-5 w-5 text-destructive" viewBox="0 0 20 20" fill="currentColor">
95-
<path fillRule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clipRule="evenodd" />
96-
</svg>
97-
</div>
98-
<div className="ml-3">
99-
<p className="text-sm text-destructive">{error}</p>
100-
</div>
101-
</div>
94+
{/* Content */}
95+
<div className="p-6">
96+
<div className="mb-6">
97+
<h3 className="text-lg font-medium text-foreground mb-2">
98+
Select server to execute &quot;{scriptName}&quot;
99+
</h3>
102100
</div>
103-
)}
104-
105-
{/* Execution Mode Selection */}
106-
<div className="space-y-4 mb-6">
107-
108101

109-
{/* SSH Execution */}
110-
<div
111-
className={`border rounded-lg p-4 cursor-pointer transition-colors ${
112-
selectedMode === 'ssh'
113-
? 'border-primary bg-primary/10'
114-
: 'border-border hover:border-primary/50'
115-
}`}
116-
onClick={() => handleModeChange('ssh')}
117-
>
118-
<div className="flex items-center">
119-
<input
120-
type="radio"
121-
id="ssh"
122-
name="executionMode"
123-
value="ssh"
124-
checked={selectedMode === 'ssh'}
125-
onChange={() => handleModeChange('ssh')}
126-
className="h-4 w-4 text-primary focus:ring-primary border-border"
127-
/>
128-
<label htmlFor="ssh" className="ml-3 flex-1 cursor-pointer">
129-
<div className="flex items-center">
130-
<div className="flex-shrink-0">
131-
<div className="w-10 h-10 bg-primary/10 rounded-full flex items-center justify-center">
132-
<svg className="w-6 h-6 text-primary" fill="none" stroke="currentColor" viewBox="0 0 24 24">
133-
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9.75 17L9 20l-1 1h8l-1-1-.75-3M3 13h18M5 17h14a2 2 0 002-2V5a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" />
134-
</svg>
135-
</div>
136-
</div>
137-
<div className="ml-3">
138-
<h4 className="text-sm font-medium text-foreground">SSH Execution</h4>
139-
<p className="text-sm text-muted-foreground">Run the script on a remote server</p>
140-
</div>
102+
{error && (
103+
<div className="mb-4 p-3 bg-destructive/10 border border-destructive/20 rounded-md">
104+
<div className="flex">
105+
<div className="flex-shrink-0">
106+
<svg className="h-5 w-5 text-destructive" viewBox="0 0 20 20" fill="currentColor">
107+
<path fillRule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clipRule="evenodd" />
108+
</svg>
109+
</div>
110+
<div className="ml-3">
111+
<p className="text-sm text-destructive">{error}</p>
141112
</div>
142-
</label>
113+
</div>
143114
</div>
144-
</div>
145-
</div>
115+
)}
146116

147-
{/* Server Selection (only for SSH mode) */}
148-
{selectedMode === 'ssh' && (
117+
{/* Server Selection */}
149118
<div className="mb-6">
150119
<label htmlFor="server" className="block text-sm font-medium text-foreground mb-2">
151120
Select Server
@@ -158,7 +127,15 @@ export function ExecutionModeModal({ isOpen, onClose, onExecute, scriptName }: E
158127
) : servers.length === 0 ? (
159128
<div className="text-center py-4 text-muted-foreground">
160129
<p className="text-sm">No servers configured</p>
161-
<p className="text-xs mt-1">Add servers in Settings to use SSH execution</p>
130+
<p className="text-xs mt-1">Add servers in Settings to execute scripts</p>
131+
<Button
132+
onClick={() => setSettingsModalOpen(true)}
133+
variant="outline"
134+
size="sm"
135+
className="mt-3"
136+
>
137+
Open Server Settings
138+
</Button>
162139
</div>
163140
) : (
164141
<select
@@ -180,29 +157,35 @@ export function ExecutionModeModal({ isOpen, onClose, onExecute, scriptName }: E
180157
</select>
181158
)}
182159
</div>
183-
)}
184160

185-
{/* Action Buttons */}
186-
<div className="flex justify-end space-x-3">
187-
<Button
188-
onClick={onClose}
189-
variant="outline"
190-
size="default"
191-
>
192-
Cancel
193-
</Button>
194-
<Button
195-
onClick={handleExecute}
196-
disabled={selectedMode === 'ssh' && !selectedServer}
197-
variant="default"
198-
size="default"
199-
className={selectedMode === 'ssh' && !selectedServer ? 'bg-gray-400 cursor-not-allowed' : ''}
200-
>
201-
{selectedMode === 'local' ? 'Run Locally' : 'Run on Server'}
202-
</Button>
161+
{/* Action Buttons */}
162+
<div className="flex justify-end space-x-3">
163+
<Button
164+
onClick={onClose}
165+
variant="outline"
166+
size="default"
167+
>
168+
Cancel
169+
</Button>
170+
<Button
171+
onClick={handleExecute}
172+
disabled={!selectedServer}
173+
variant="default"
174+
size="default"
175+
className={!selectedServer ? 'bg-gray-400 cursor-not-allowed' : ''}
176+
>
177+
Run on Server
178+
</Button>
179+
</div>
203180
</div>
204181
</div>
205182
</div>
206-
</div>
183+
184+
{/* Server Settings Modal */}
185+
<SettingsModal
186+
isOpen={settingsModalOpen}
187+
onClose={handleSettingsModalClose}
188+
/>
189+
</>
207190
);
208191
}

src/app/_components/Terminal.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -314,7 +314,6 @@ export function Terminal({ scriptPath, onClose, mode = 'local', server, isUpdate
314314
ws.onmessage = (event) => {
315315
try {
316316
const message = JSON.parse(event.data as string) as TerminalMessage;
317-
console.log('WebSocket message received:', message);
318317
handleMessage(message);
319318
} catch (error) {
320319
console.error('Error parsing WebSocket message:', error);

src/server/api/routers/installedScripts.ts

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -268,15 +268,13 @@ export const installedScriptsRouter = createTRPCRouter({
268268
server as any,
269269
command,
270270
(data: string) => {
271-
console.log('Command output chunk:', data);
272271
commandOutput += data;
273272
},
274273
(error: string) => {
275274
console.error('Command error:', error);
276275
},
277276
(exitCode: number) => {
278277
console.log('Command exit code:', exitCode);
279-
console.log('Full command output:', commandOutput);
280278

281279
// Parse the complete output to get config file paths that contain community-script tag
282280
const configFiles = commandOutput.split('\n')
@@ -306,8 +304,6 @@ export const installedScriptsRouter = createTRPCRouter({
306304
server as any,
307305
readCommand,
308306
(configData: string) => {
309-
console.log('Config data for', containerId, ':', configData.substring(0, 300) + '...');
310-
311307
// Parse config file for hostname
312308
const lines = configData.split('\n');
313309
let hostname = '';
@@ -316,7 +312,6 @@ export const installedScriptsRouter = createTRPCRouter({
316312
const trimmedLine = line.trim();
317313
if (trimmedLine.startsWith('hostname:')) {
318314
hostname = trimmedLine.substring(9).trim();
319-
console.log('Found hostname for', containerId, ':', hostname);
320315
break;
321316
}
322317
}
@@ -368,7 +363,6 @@ export const installedScriptsRouter = createTRPCRouter({
368363

369364
// Get existing scripts to check for duplicates
370365
const existingScripts = db.getAllInstalledScripts();
371-
console.log('Existing scripts in database:', existingScripts.length);
372366

373367
// Create installed script records for detected containers (skip duplicates)
374368
const createdScripts = [];
@@ -504,7 +498,6 @@ export const installedScriptsRouter = createTRPCRouter({
504498
server as any,
505499
checkCommand,
506500
(data: string) => {
507-
console.log(`Container check result for ${scriptData.script_name}:`, data.trim());
508501
resolve(data.trim() === 'exists');
509502
},
510503
(error: string) => {

0 commit comments

Comments
 (0)