feat: Server Color Coding System (#103)

* feat: implement server color coding feature

- Add color column to servers table with migration
- Add SERVER_COLOR_CODING_ENABLED environment variable
- Create API route for color coding toggle settings
- Add color field to Server and CreateServerData types
- Update database CRUD operations to handle color field
- Update server API routes to handle color field
- Create colorUtils.ts with contrast calculation function
- Add color coding toggle to GeneralSettingsModal
- Add color picker to ServerForm component (only shown when enabled)
- Apply colors to InstalledScriptsTab (borders and server column)
- Apply colors to ScriptInstallationCard component
- Apply colors to ServerList component
- Fix 'Local' display issue in installed scripts table

* fix: resolve TypeScript errors in color coding implementation

- Fix unsafe argument type errors in GeneralSettingsModal and ServerForm
- Remove unused import in ServerList component

* feat: add color-coded dropdown for server selection

- Create ColorCodedDropdown component with server color indicators
- Replace HTML select with custom dropdown in ExecutionModeModal
- Add color dots next to server names in dropdown options
- Maintain all existing functionality with improved visual design

* fix: generate new execution ID for each script run

- Change executionId from useState to allow updates
- Generate new execution ID in startScript function for each run
- Fixes issue where scripts couldn't be run multiple times without page reload
- Resolves 'Script execution already running' error on subsequent runs

* fix: improve whiptail handling and execution ID generation

- Remove premature terminal clearing for whiptail sessions
- Let whiptail handle its own display without interference
- Generate new execution ID for both initial and manual script runs
- Fix whiptail session state management
- Should resolve blank screen and script restart issues

* fix: revert problematic whiptail changes that broke terminal display

- Remove complex whiptail session handling that caused blank screen
- Simplify output handling to just write data directly to terminal
- Keep execution ID generation fix for multiple script runs
- Remove unused inWhiptailSession state variable
- Terminal should now display output normally again

* fix: remove remaining inWhiptailSession reference

- Remove inWhiptailSession from useEffect dependency array
- Fixes ReferenceError: inWhiptailSession is not defined
- Terminal should now work without JavaScript errors

* debug: add console logging to terminal message handling

- Add debug logs to see what messages are being received
- Help diagnose why terminal shows blank screen
- Will remove debug logs once issue is identified

* fix: prevent WebSocket reconnection loop

- Remove executionId from useEffect dependency arrays
- Fixes terminal constantly reconnecting and showing blank screen
- WebSocket now maintains stable connection during script execution
- Removes debug console logs

* fix: prevent WebSocket reconnection on second script run

- Remove handleMessage from useEffect dependency array
- Fixes loop of START messages and connection blinking on subsequent runs
- WebSocket connection now stable for multiple script executions
- handleMessage recreation no longer triggers WebSocket reconnection

* debug: add logging to identify WebSocket reconnection cause

- Add console logs to useEffect and startScript
- Track what dependencies are changing
- Identify why WebSocket reconnects on second run

* fix: remove isRunning from WebSocket useEffect dependencies

- isRunning state change was causing WebSocket reconnection loop
- Each script start changed isRunning from false to true
- This triggered useEffect to reconnect WebSocket
- Removing isRunning from dependencies breaks the loop
- WebSocket connection now stable during script execution

* feat: preselect SSH mode in execution modal and clean up debug logs

- Preselect SSH execution mode by default since it's the only available option
- Remove debug console logs from Terminal component
- Clean up code for production readiness

* fix: resolve build errors and warnings

- Add missing SettingsModal import to ExecutionModeModal
- Remove unused selectedMode and handleModeChange variables
- Add ESLint disable comments for intentional useEffect dependency exclusions
- Build now passes successfully with no errors or warnings
This commit is contained in:
Michel Roegl-Brunner
2025-10-10 14:30:28 +02:00
committed by GitHub
parent aa9e155b0c
commit 4faa74b4c5
15 changed files with 421 additions and 64 deletions

View File

@@ -52,7 +52,7 @@ export async function PUT(
}
const body = await request.json();
const { name, ip, user, password, auth_type, ssh_key, ssh_key_passphrase, ssh_port }: CreateServerData = body;
const { name, ip, user, password, auth_type, ssh_key, ssh_key_passphrase, ssh_port, color }: CreateServerData = body;
// Validate required fields
if (!name || !ip || !user) {
@@ -120,7 +120,8 @@ export async function PUT(
auth_type: authType,
ssh_key,
ssh_key_passphrase,
ssh_port: ssh_port ?? 22
ssh_port: ssh_port ?? 22,
color
});
return NextResponse.json(

View File

@@ -20,7 +20,7 @@ export async function GET() {
export async function POST(request: NextRequest) {
try {
const body = await request.json();
const { name, ip, user, password, auth_type, ssh_key, ssh_key_passphrase, ssh_port }: CreateServerData = body;
const { name, ip, user, password, auth_type, ssh_key, ssh_key_passphrase, ssh_port, color }: CreateServerData = body;
// Validate required fields
if (!name || !ip || !user) {
@@ -78,7 +78,8 @@ export async function POST(request: NextRequest) {
auth_type: authType,
ssh_key,
ssh_key_passphrase,
ssh_port: ssh_port ?? 22
ssh_port: ssh_port ?? 22,
color
});
return NextResponse.json(

View File

@@ -0,0 +1,75 @@
import type { NextRequest } from 'next/server';
import { NextResponse } from 'next/server';
import fs from 'fs';
import path from 'path';
export async function POST(request: NextRequest) {
try {
const { enabled } = await request.json();
if (typeof enabled !== 'boolean') {
return NextResponse.json(
{ error: 'Enabled must be a boolean value' },
{ status: 400 }
);
}
// Path to the .env file
const envPath = path.join(process.cwd(), '.env');
// Read existing .env file
let envContent = '';
if (fs.existsSync(envPath)) {
envContent = fs.readFileSync(envPath, 'utf8');
}
// Check if SERVER_COLOR_CODING_ENABLED already exists
const colorCodingRegex = /^SERVER_COLOR_CODING_ENABLED=.*$/m;
const colorCodingMatch = colorCodingRegex.exec(envContent);
if (colorCodingMatch) {
// Replace existing SERVER_COLOR_CODING_ENABLED
envContent = envContent.replace(colorCodingRegex, `SERVER_COLOR_CODING_ENABLED=${enabled}`);
} else {
// Add new SERVER_COLOR_CODING_ENABLED
envContent += (envContent.endsWith('\n') ? '' : '\n') + `SERVER_COLOR_CODING_ENABLED=${enabled}\n`;
}
// Write back to .env file
fs.writeFileSync(envPath, envContent);
return NextResponse.json({ success: true, message: 'Color coding setting saved successfully' });
} catch (error) {
console.error('Error saving color coding setting:', error);
return NextResponse.json(
{ error: 'Failed to save color coding setting' },
{ status: 500 }
);
}
}
export async function GET() {
try {
// Path to the .env file
const envPath = path.join(process.cwd(), '.env');
if (!fs.existsSync(envPath)) {
return NextResponse.json({ enabled: false });
}
const envContent = fs.readFileSync(envPath, 'utf8');
// Extract SERVER_COLOR_CODING_ENABLED
const colorCodingRegex = /^SERVER_COLOR_CODING_ENABLED=(.*)$/m;
const colorCodingMatch = colorCodingRegex.exec(envContent);
const enabled = colorCodingMatch ? colorCodingMatch[1]?.trim().toLowerCase() === 'true' : false;
return NextResponse.json({ enabled });
} catch (error) {
console.error('Error reading color coding setting:', error);
return NextResponse.json(
{ error: 'Failed to read color coding setting' },
{ status: 500 }
);
}
}