Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
156 changes: 156 additions & 0 deletions src/Projects/WebSocketCLI/cli-client.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
<!DOCTYPE html>
<html>
<head>
<title>WebSocket CLI</title>
<style>
* { box-sizing: border-box; }
body {
font-family: 'Courier New', monospace;
background: #1a1a2e;
color: #0f0;
margin: 0;
padding: 20px;
height: 100vh;
}
h1 { color: #00ff88; margin-bottom: 10px; }
#status {
padding: 5px 10px;
border-radius: 4px;
display: inline-block;
margin-bottom: 10px;
}
.connected { background: #004400; }
.disconnected { background: #440000; }
#terminal {
background: #0d0d1a;
border: 1px solid #333;
border-radius: 8px;
height: calc(100vh - 200px);
overflow-y: auto;
padding: 15px;
margin-bottom: 10px;
}
.output { color: #ccc; margin: 2px 0; }
.command { color: #00ff88; }
.error { color: #ff4444; }
.info { color: #4488ff; }
#inputRow { display: flex; gap: 10px; }
#commandInput {
flex: 1;
background: #0d0d1a;
border: 1px solid #444;
color: #0f0;
padding: 12px;
font-family: inherit;
font-size: 14px;
border-radius: 4px;
}
button {
background: #00aa55;
border: none;
color: white;
padding: 12px 24px;
cursor: pointer;
border-radius: 4px;
font-weight: bold;
}
button:hover { background: #00cc66; }
#connectBtn { background: #4488ff; }
#connectBtn:hover { background: #5599ff; }
</style>
</head>
<body>
<h1>WebSocket CLI Runner</h1>
<div>
<input type="text" id="serverUrl" value="ws://localhost:8080/cli" style="width: 300px; padding: 8px; background: #222; color: #fff; border: 1px solid #444;">
<button id="connectBtn" onclick="toggleConnection()">Connect</button>
<span id="status" class="disconnected">Disconnected</span>
</div>

<div id="terminal"></div>

<div id="inputRow">
<input type="text" id="commandInput" placeholder="Enter command..." onkeypress="if(event.key==='Enter')sendCommand()">
<button onclick="sendCommand()">Run</button>
<button onclick="clearTerminal()" style="background:#666">Clear</button>
</div>

<script>
let ws = null;
const terminal = document.getElementById('terminal');
const input = document.getElementById('commandInput');
const status = document.getElementById('status');
const connectBtn = document.getElementById('connectBtn');

function log(text, className = 'output') {
const div = document.createElement('div');
div.className = className;
div.textContent = text;
terminal.appendChild(div);
terminal.scrollTop = terminal.scrollHeight;
}

function toggleConnection() {
if (ws && ws.readyState === WebSocket.OPEN) {
ws.close();
} else {
connect();
}
}

function connect() {
const url = document.getElementById('serverUrl').value;
log(`Connecting to ${url}...`, 'info');

ws = new WebSocket(url);

ws.onopen = () => {
status.textContent = 'Connected';
status.className = 'connected';
connectBtn.textContent = 'Disconnect';
log('Connected!', 'info');
};

ws.onclose = () => {
status.textContent = 'Disconnected';
status.className = 'disconnected';
connectBtn.textContent = 'Connect';
log('Disconnected', 'info');
};

ws.onerror = (e) => {
log('Connection error', 'error');
};

ws.onmessage = (event) => {
const data = JSON.parse(event.data);
if (data.type === 'output') {
log(data.data);
} else if (data.type === 'completed') {
log('--- Command completed ---', 'info');
} else if (data.type === 'error') {
log(data.data, 'error');
} else if (data.type === 'connected') {
log(data.data, 'info');
}
};
}

function sendCommand() {
const cmd = input.value.trim();
if (!cmd || !ws || ws.readyState !== WebSocket.OPEN) return;

log(`$ ${cmd}`, 'command');
ws.send(JSON.stringify({ command: cmd }));
input.value = '';
}

function clearTerminal() {
terminal.innerHTML = '';
}

// Auto-connect on load
setTimeout(connect, 500);
</script>
</body>
</html>
71 changes: 71 additions & 0 deletions src/Projects/WebSocketCLI/cli-client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
#!/usr/bin/env python3
"""
WebSocket CLI Client
Usage: python cli-client.py [server-url]
Default: ws://localhost:8080/cli
"""

import asyncio
import websockets
import json
import sys

DEFAULT_URL = "ws://localhost:8080/cli"

async def cli_client(url):
print(f"Connecting to {url}...")

try:
async with websockets.connect(url) as ws:
print("Connected! Type commands (or 'exit' to quit)\n")

# Task to receive messages
async def receiver():
try:
async for message in ws:
data = json.loads(message)
msg_type = data.get('type', '')
content = data.get('data', '')

if msg_type == 'output':
print(f" {content}")
elif msg_type == 'completed':
print(f"\n[Done]\n> ", end='', flush=True)
elif msg_type == 'error':
print(f"[ERROR] {content}")
elif msg_type == 'connected':
print(f"[Server] {content}\n> ", end='', flush=True)
else:
print(f"[{msg_type}] {content}")
except websockets.exceptions.ConnectionClosed:
print("\nConnection closed")

# Start receiver task
recv_task = asyncio.create_task(receiver())

# Input loop
loop = asyncio.get_event_loop()
while True:
try:
cmd = await loop.run_in_executor(None, input, "")

if cmd.lower() == 'exit':
print("Bye!")
break

if cmd.strip():
await ws.send(json.dumps({"command": cmd}))

except EOFError:
break

recv_task.cancel()

except ConnectionRefusedError:
print(f"Cannot connect to {url}. Is the server running?")
except Exception as e:
print(f"Error: {e}")

if __name__ == "__main__":
url = sys.argv[1] if len(sys.argv) > 1 else DEFAULT_URL
asyncio.run(cli_client(url))
56 changes: 56 additions & 0 deletions src/Projects/WebSocketCLI/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.0</version>
<relativePath/>
</parent>

<groupId>org.csystem</groupId>
<artifactId>websocket-cli</artifactId>
<version>1.0.0</version>
<name>WebSocket CLI Runner</name>
<description>WebSocket based remote CLI runner</description>

<properties>
<java.version>17</java.version>
</properties>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.10.1</version>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package org.csystem.app;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
System.out.println("WebSocket CLI Runner started on ws://localhost:8080/cli");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package org.csystem.app.config;

import org.csystem.app.handler.CLIWebSocketHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
import org.springframework.web.socket.server.standard.ServletServerContainerFactoryBean;

@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {

private final CLIWebSocketHandler cliWebSocketHandler;

public WebSocketConfig(CLIWebSocketHandler cliWebSocketHandler) {
this.cliWebSocketHandler = cliWebSocketHandler;
}

@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(cliWebSocketHandler, "/cli")
.setAllowedOrigins("*");
}

@Bean
public ServletServerContainerFactoryBean createWebSocketContainer() {
ServletServerContainerFactoryBean container = new ServletServerContainerFactoryBean();
container.setMaxTextMessageBufferSize(65536);
container.setMaxBinaryMessageBufferSize(65536);
container.setMaxSessionIdleTimeout(600000L);
return container;
}
}
Loading