Spaces:
Running
Running
import asyncio | |
import json | |
import os | |
import secrets | |
import signal | |
import subprocess | |
import websockets | |
import shutil | |
from connect4 import PLAYER1, PLAYER2, Connect4 | |
from user import User | |
import requests | |
from watchdog.observers import Observer | |
from watchdog.events import FileSystemEventHandler | |
from bs4 import BeautifulSoup | |
from urllib.parse import urlparse, urljoin | |
import math | |
import uuid | |
def is_absolute(url): | |
return bool(urlparse(url).netloc) | |
def handle_protocol_relative(url, base_url): | |
if url.startswith("//"): | |
parsed_base_url = urlparse(base_url) | |
return f"{parsed_base_url.scheme}:{url}" | |
return url | |
def inline_resources(soup, base_url): | |
# Inline CSS | |
for link_tag in soup.find_all('link', rel='stylesheet'): | |
href = link_tag.get('href') | |
if href: | |
href = handle_protocol_relative(href, base_url) | |
full_url = urljoin(base_url, href) if not is_absolute(href) else href | |
css_response = requests.get(full_url) | |
if css_response.status_code == 200: | |
style_tag = soup.new_tag('style') | |
style_tag.string = css_response.text | |
link_tag.replace_with(style_tag) | |
# Inline JavaScript | |
for script_tag in soup.find_all('script', src=True): | |
src = script_tag.get('src') | |
if src: | |
src = handle_protocol_relative(src, base_url) | |
full_url = urljoin(base_url, src) if not is_absolute(src) else src | |
js_response = requests.get(full_url) | |
if js_response.status_code == 200: | |
script_tag.string = js_response.text | |
del script_tag['src'] | |
return soup | |
async def send_html_chunks(websocket, soup): | |
# Convert the BeautifulSoup object back to a string and split into lines | |
html_str = str(soup) | |
lines = html_str.split('\n') | |
# Send each line as a separate chunk | |
for line in lines: | |
line = line.strip() | |
if line: # Send non-empty lines | |
await websocket.send(json.dumps({"type": "chunk", "content": line})) | |
await websocket.send(json.dumps({"type": "end",})) | |
async def send_message_chunks(websocket, message): | |
msg_id = str(uuid.uuid4()) | |
message_json = json.dumps(message) | |
chunks = [message_json[i:i+10] for i in range(0, len(message_json), 10)] | |
for i, chunk in enumerate(chunks): | |
chunk_data = { | |
'msg_id': msg_id, | |
'part': chunk, | |
'end': i == len(chunks) - 1 | |
} | |
await websocket.send(json.dumps(chunk_data)) | |
print(f"Sent chunk: {chunk_data}") | |
def auto_detect_mode(content): | |
if isinstance(content, str): | |
return 'w' # If content is a string, it's text | |
elif isinstance(content, bytes): | |
return 'wb' # If content is bytes, it's binary | |
else: | |
raise ValueError("Unsupported content type. Expected str or bytes.") | |
class FileHandler(FileSystemEventHandler): | |
def __init__(self, websocket,connected): | |
super().__init__() | |
self.websocket = websocket | |
self.connected = connected | |
def on_modified(self, event): | |
if event.is_directory: | |
return | |
with open(event.src_path, 'r', encoding='utf-8') as file: | |
content = file.read() | |
print(f'File {event.src_path} has been modified.') | |
new_event = { | |
"type": "file-modifie", | |
"content": content, | |
"path": event.src_path, | |
} | |
websockets.broadcast(self.connected,json.dumps(new_event)) | |
# websockets.broadcast(self.connected,f'File {event.src_path} has been modified.') | |
def on_created(self, event): | |
if event.is_directory: | |
new_event = { | |
"type": "folder-create", | |
"path": event.src_path, | |
"name": event.src_path.split('/')[-1] | |
} | |
# print(new_event) | |
websockets.broadcast(self.connected,json.dumps(new_event)) | |
else: | |
with open(event.src_path, 'r', encoding='utf-8') as file: | |
content = file.read() | |
print(f'File {event.src_path} has been created.') | |
new_event = { | |
"type": "file-create", | |
"content": content, | |
"path": event.src_path, | |
"name": event.src_path.split('/')[-1] | |
} | |
# print(new_event) | |
websockets.broadcast(self.connected,json.dumps(new_event)) | |
# websockets.broadcast(self.connected,f'File {event.src_path} has been created.') | |
def on_deleted(self, event): | |
if event.is_directory: | |
new_event = { | |
"type": "delete", | |
"path": event.src_path, | |
"name": event.src_path.split('/')[-1], | |
} | |
websockets.broadcast(self.connected,json.dumps(new_event)) | |
else: | |
print(f'File {event.src_path} has been deleted.') | |
new_event = { | |
"type": "delete", | |
"name": event.src_path.split('/')[-1], | |
"path": event.src_path, | |
} | |
print(event.src_path) | |
websockets.broadcast(self.connected,json.dumps(new_event)) | |
def on_moved(self, event): | |
if event.is_directory: | |
new_event = { | |
"type": "rename", | |
"OldPath": event.src_path, | |
"oldname": event.src_path.split('/')[-1], | |
"NewPath": event.dest_path, | |
"newname": event.dest_path.split('/')[-1], | |
} | |
websockets.broadcast(self.connected,json.dumps(new_event)) | |
else: | |
# print(f'File {event.src_path} has been renamed to {event.dest_path}.') | |
new_event = { | |
"type": "rename", | |
"OldPath": event.src_path, | |
"oldname": event.src_path.split('/')[-1], | |
"NewPath": event.dest_path, | |
"newname": event.dest_path.split('/')[-1], | |
} | |
websockets.broadcast(self.connected,json.dumps(new_event)) | |
JOIN = {} | |
WATCH = {} | |
async def generate_file_structure(path, encoding='utf-8'): | |
file_structure = {'name': os.path.basename(path), 'type': 'folder', 'path': path, 'children': []} | |
try: | |
entries = os.listdir(path) | |
except FileNotFoundError: | |
return file_structure # Return an empty structure for non-existing directories | |
for entry in entries: | |
entry_path = os.path.join(path, entry) | |
if os.path.isdir(entry_path): | |
child_structure =await generate_file_structure(entry_path, encoding) | |
file_structure['children'].append(child_structure) | |
elif os.path.isfile(entry_path): | |
try: | |
with open(entry_path, 'r', encoding=encoding) as file: | |
content = file.read() | |
except UnicodeDecodeError: | |
content = 'Unable to read content' | |
file_structure['children'].append({'name': entry, 'type': 'file', 'path': entry_path, 'content': content}) | |
return file_structure | |
async def rename_item(websocket, key,project_name, path,rpath,new_name,root, connected): | |
old_path = os.path.join(os.getcwd(),'projects', key,project_name, rpath) | |
new_name = new_name | |
new_path = os.path.join(os.path.dirname(old_path), new_name) | |
try: | |
if os.path.exists(old_path): | |
# Determine the new path | |
# Rename the file or folder | |
os.rename(old_path, new_path) | |
websockets.broadcast(connected,'success') | |
event = { | |
"type": "rename-success", | |
"data": f'{old_path}-->{new_path}', | |
"path": path, | |
"new_rpath":rpath, | |
"name": new_name, | |
"root":root, | |
} | |
websockets.broadcast(connected,json.dumps(event)) | |
else: | |
event = { | |
"type": "rename-failed", | |
"data": f'{old_path}-->{new_path} failed Item not found', | |
"old_path": path, | |
"new_name": new_name, | |
} | |
websockets.broadcast(connected,json.dumps(event)) | |
except Exception as e: | |
websockets.broadcast(connected,str(e)) | |
async def delete_item(websocket, key,project_name, path,rpath,targetElementData, connected): | |
try: | |
item_path = os.path.join(os.getcwd(), 'projects', key,project_name, rpath) | |
if os.path.exists(item_path): | |
if os.path.isdir(item_path): | |
shutil.rmtree(item_path) # Remove the directory and its contents | |
elif os.path.isfile(item_path): | |
os.remove(item_path) # Remove the file | |
event = { | |
"type": "delete-success", | |
"data": path, | |
"path":path, | |
"targetElementData":targetElementData, | |
} | |
# print(event) | |
websockets.broadcast(connected,json.dumps(event)) | |
# websockets.broadcast(connected,'success') | |
else: | |
event = { | |
"type": "delete-failed", | |
"data": f'{item_path} Item not found', | |
} | |
websockets.broadcast(connected,json.dumps(event)) | |
except Exception as e: | |
# print(e) | |
websockets.broadcast(connected,str(e)) | |
async def get_file_content(websocket, key,project_name, path,rpath,name,connected): | |
file_path = os.path.join(os.getcwd(), 'projects', key,project_name, rpath) | |
try: | |
try: | |
with open(file_path, 'r') as file: | |
content = file.read() | |
event = { | |
"type": "content", | |
"content": content, | |
'fileName':name, | |
'rfilePath':rpath, | |
'filePath':path, | |
} | |
await send_message_chunks(websocket,event) | |
except UnicodeDecodeError: | |
with open(file_path, 'rb') as file: | |
content = file.read() | |
event = { | |
"type": "content", | |
"content": content, | |
'fileName':name, | |
'rfilePath':rpath, | |
'filePath':path, | |
} | |
await send_message_chunks(websocket,event) | |
except Exception as e: | |
event = { | |
"type": "error", | |
"message": f"Failed to read file content: {str(e)}", | |
} | |
await send_message_chunks(websocket,event) | |
async def create_file(websocket, key,project_name, path,name,root,targetElementData,rpath, connected): | |
file_path = os.path.join(os.getcwd(), 'projects', key,project_name, rpath,name) | |
# Create the file | |
with open(file_path, 'w'): | |
pass | |
event = { | |
"type": "file-created", | |
"data": file_path, | |
"path": path, | |
"name": name, | |
"root":root, | |
"targetElementData":targetElementData, | |
} | |
websockets.broadcast(connected,json.dumps(event)) | |
async def create_folder(websocket, key,project_name, path,name,root,targetElementData,rpath, connected): | |
folder_path = os.path.join(os.getcwd(), 'projects', key,project_name, rpath,name) | |
# Create the folder | |
os.makedirs(folder_path) | |
event = { | |
"type": "folder-created", | |
"data": folder_path, | |
"path": path, | |
"name": name, | |
"root":root, | |
"targetElementData":targetElementData, | |
} | |
# print(folder_path,'created') | |
websockets.broadcast(connected,json.dumps(event)) | |
async def wirte_file(websocket, key,project_name, path, content, connected): | |
try: | |
file_path = os.path.join(os.getcwd(), 'projects', key,project_name, path) | |
file_content = content | |
mode = auto_detect_mode(content) | |
with open(file_path, mode) as file: | |
file.write(file_content) | |
event = { | |
"type": "write-success", | |
"data": file_path, | |
"path": path, | |
"content": content, | |
} | |
# websockets.broadcast(connected,json.dumps(event)) | |
except FileNotFoundError as e: | |
event = { | |
"type": "write-error", | |
"data": e, | |
} | |
websockets.broadcast(connected,json.dumps(event)) | |
async def read_process_output(process, websocket): | |
# Read the output and error byte by byte and print word by word | |
word = b'' | |
while True: | |
char = await process.stdout.read(1) | |
if char == b'\n': | |
if word: | |
print(word.decode('utf-8'),1) | |
event = { | |
"type": "terminal-data", | |
"data": word.decode('utf-8'), | |
} | |
print('sending data',1) | |
await send_message_chunks(websocket,event) | |
word = b'' | |
print("line is completed") | |
event = { | |
"type": "terminal-newline", | |
"data": "", | |
} | |
await send_message_chunks(websocket,event) | |
elif char == b' ': | |
if word: | |
print(word.decode('utf-8'),2) | |
event = { | |
"type": "terminal-data", | |
"data": word.decode('utf-8'), | |
} | |
print('sending data',2) | |
await send_message_chunks(websocket,event) | |
word = b'' | |
print("space is occurred") | |
event = { | |
"type": "terminal-data", | |
"data": " ", | |
} | |
print('sending data',3) | |
await send_message_chunks(websocket,event) | |
elif char == b'': | |
if word: | |
print(word.decode('utf-8'),3) | |
event = { | |
"type": "terminal-data", | |
"data": word.decode('utf-8'), | |
} | |
print('sending data',4) | |
await send_message_chunks(websocket,event) | |
break | |
else: | |
word += char | |
while True: | |
char = await process.stderr.read(1) | |
if char == b'\n': | |
if word: | |
print(word.decode('utf-8')) | |
event = { | |
"type": "terminal-error", | |
"data": word.decode('utf-8'), | |
} | |
await send_message_chunks(websocket,event) | |
word = b'' | |
print("line is completed") | |
event = { | |
"type": "terminal-newline", | |
"data": "", | |
} | |
await send_message_chunks(websocket,event) | |
elif char == b' ': | |
if word: | |
print(word.decode('utf-8')) | |
event = { | |
"type": "terminal-error", | |
"data": word.decode('utf-8'), | |
} | |
await send_message_chunks(websocket,event) | |
word = b'' | |
print("space is occurred") | |
event = { | |
"type": "terminal-error", | |
"data": " ", | |
} | |
await send_message_chunks(websocket,event) | |
elif char == b'': | |
if word: | |
print(word.decode('utf-8')) | |
break | |
else: | |
word += char | |
# async for line in process.stdout: | |
# # print('sending line') | |
# event = { | |
# "type": "terminal-data", | |
# "data": line.strip().decode('utf-8'), | |
# } | |
# await send_message_chunks(websocket,event) | |
# async for line in process.stderr: | |
# # print(f'error:{line.strip()}') | |
# event = { | |
# "type": "terminal-error", | |
# "data": line.strip().decode('utf-8'), | |
# } | |
# await send_message_chunks(websocket,event) | |
async def handle_user_input(websocket,key, process, connected,process_ids): | |
while True: | |
try: | |
await asyncio.sleep(0.1) | |
if process.returncode is not None: | |
break | |
# message = await websocket.recv() | |
async for message in websocket: | |
# user_input = json.loads(message) | |
# print(user_input) | |
# print(f'Received user input: {user_input["command"]["command"]}') | |
# process_input(user_input["command"]["command"], process) | |
event = json.loads(message) | |
assert event["type"] == "cmd" | |
# command = event["command"] | |
print(f'Received user input: {event}') | |
try: | |
if event["command"]["type"]=="shell": | |
await asyncio.sleep(0.1) | |
if process.returncode is not None: | |
base_path = os.path.join(os.getcwd(), 'projects', key,event["project_name"]) | |
directory_path = base_path | |
event_handler = FileHandler(websocket,connected) | |
observer = Observer() | |
observer.schedule(event_handler, path=directory_path, recursive=True) | |
observer.start() | |
try: | |
# await execute_command(websocket, key,event["project_name"], event["command"]["command"], connected) | |
# base_path = os.path.join(os.getcwd(), 'projects', key,event["project_name"]) | |
print(base_path) | |
mod_command = f'cd {base_path} && {event["command"]["command"]}' | |
print(mod_command) | |
try: | |
process = await asyncio.create_subprocess_shell( | |
mod_command, | |
# cwd=base_path, | |
stdin=asyncio.subprocess.PIPE, | |
stdout=asyncio.subprocess.PIPE, | |
stderr=asyncio.subprocess.PIPE, | |
# text=True, | |
) | |
id=process.pid | |
process_ids.append(id) | |
async def send_message(message): | |
# print('sending msg') | |
# await websocket.send(f'data: {message}') | |
websockets.broadcast(connected,json.dumps(message) ) | |
await asyncio.gather( | |
handle_user_input(websocket,key, process, connected,process_ids), | |
read_process_output(process, websocket) | |
) | |
return_code = await process.wait() | |
if return_code == 0: | |
event = { | |
"type": "terminal-data-end", | |
} | |
await send_message_chunks(websocket,event) | |
else: | |
event = { | |
"type": "terminal-data-end", | |
} | |
await send_message_chunks(websocket,event) | |
process_ids.remove(id) | |
except Exception as e: | |
await error(websocket, str(e)) | |
except KeyboardInterrupt: | |
pass # Handle KeyboardInterrupt to gracefully stop the observer | |
observer.stop() | |
observer.join() | |
else: | |
await process_input(event["command"]["command"], process) | |
elif event["command"]["type"]=="write": | |
await wirte_file(websocket, key,event["project_name"], event["path"], event["content"], connected) | |
elif event["command"]["type"] == "curl": | |
method = event.get("method", "GET") | |
url = event["url"] | |
body = event.get("data", "") | |
headers = event.get("headers", {}) | |
if method.upper() == "GET": | |
response = requests.get(url, headers=headers) | |
elif method.upper() == "POST": | |
response = requests.post(url, data=body, headers=headers) | |
else: | |
response = {"error": "Unsupported method"} | |
if isinstance(response, dict): | |
response_data = { | |
"type": "error", | |
"data": response, | |
} | |
await websocket.send(json.dumps(response_data)) | |
return | |
# Parse HTML content | |
souptmp = BeautifulSoup(response.text, "html.parser") | |
# Step 1: Define the initial HTML string | |
html_str = """ | |
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
</head> | |
<body> | |
<h1>Hello, World!</h1> | |
</body> | |
</html> | |
""" | |
# Step 2: Parse the initial HTML content | |
soup = BeautifulSoup(html_str, 'html.parser') | |
# Step 3: Replace the existing body content with the new content from souptmp | |
if soup.body: | |
soup.body.clear() | |
soup.body.append(souptmp) | |
else: | |
print("No <body> tag found in the HTML") | |
# Inline CSS and JavaScript | |
soup = inline_resources(soup, url) | |
response_data = { | |
"type": "web-data", | |
"data": str(soup), | |
} | |
await websocket.send(json.dumps(response_data)) | |
# Convert the final soup to a string | |
elif event["command"]["type"]=="create": | |
if event["item"]=="folder": | |
await create_folder(websocket, key,event["project_name"], event["path"],event["name"],event['root'],event['targetElementData'],event["rpath"], connected) | |
else: | |
await create_file(websocket, key,event["project_name"], event["path"],event["name"],event['root'],event['targetElementData'],event["rpath"], connected) | |
elif event["command"]["type"]=="delete": | |
await delete_item(websocket, key,event["project_name"], event["path"],event['rpath'],event['targetElementData'], connected) | |
elif event["command"]["type"]=="get_content": | |
await get_file_content(websocket, key,event["project_name"],event["filePath"], event["rfilePath"],event["fileName"] ,connected) | |
elif event["command"]["type"]=="rename": | |
await rename_item(websocket, key,event["project_name"], event["path"],event['rpath'], event["name"], event["root"], connected) | |
elif event["command"]["type"]=="join": | |
await join(websocket, event["join"]) | |
elif event["command"]["type"]=="sendDir": | |
data=json.loads(event["file_structure"]) | |
event = { | |
"type": "createDir", | |
"path": data, | |
"root":event['root'], | |
} | |
# websockets.broadcast(connected,json.dumps(event)) | |
websockets.broadcast(connected, json.dumps(event)) | |
elif event["command"]["type"]=="createItemUI": | |
event = { | |
"type": "createItemUI", | |
'targetElementData':event['targetElementData'], | |
'data':event['data'] | |
} | |
# websockets.broadcast(connected,json.dumps(event)) | |
websockets.broadcast(connected, json.dumps(event)) | |
elif event["command"]["type"]=="renameItemInUI": | |
event = { | |
"type": "renameItemInUI", | |
'path':event['path'], | |
'new_path':event['new_path'], | |
'name':event['name'], | |
'new_rpath':event['new_rpath'], | |
} | |
# websockets.broadcast(connected,json.dumps(event)) | |
websockets.broadcast(connected, json.dumps(event)) | |
elif event["command"]["type"]=="createFolderUI": | |
event = { | |
"type": "createFolderUI", | |
'targetElementData':event['targetElementData'], | |
'data':event['data'] | |
} | |
# websockets.broadcast(connected,json.dumps(event)) | |
websockets.broadcast(connected, json.dumps(event)) | |
elif event["command"]["type"]=="removeItemFromUI": | |
event = { | |
"type": "removeItemFromUI", | |
'targetElementData':event['targetElementData'], | |
'path':event['path'] | |
} | |
# websockets.broadcast(connected,json.dumps(event)) | |
websockets.broadcast(connected, json.dumps(event)) | |
elif event["command"]["type"]=="project": | |
base_path = os.path.join(os.getcwd(), 'projects',key, event["project_name"]) | |
data=json.loads(event["file_structure"]) | |
await create_file_structure(websocket,data, base_path=base_path) | |
# await rename_item(websocket, key,event["project_name"], event["path"], event["name"], connected) | |
elif event["command"]["type"]=="collabration": | |
event = { | |
"type": "collabration", | |
'name': event["name"], | |
'line': event["cursorPos-line"], | |
'ch': event["cursorPos-ch"], | |
'file':event["file"], | |
'content': event["content"], | |
'color':event["color"] | |
} | |
websockets.broadcast(connected, json.dumps(event)) | |
elif event["command"]["type"]=="cursor": | |
event = { | |
"type": "cursor", | |
'offset': event["offset"], | |
'userid': event["userid"], | |
'userlabel': event["userlabel"], | |
'usercolor': event["usercolor"], | |
'fileName':event["fileName"], | |
} | |
websockets.broadcast(connected, json.dumps(event)) | |
elif event["command"]["type"]=="select": | |
event = { | |
"type": "select", | |
'id': event["id"], | |
'startOffset': event["startOffset"], | |
'endOffset': event["endOffset"], | |
'userid': event["userid"], | |
'userlabel': event["userlabel"], | |
'usercolor': event["usercolor"], | |
'fileName':event["fileName"], | |
} | |
websockets.broadcast(connected, json.dumps(event)) | |
elif event["command"]["type"]=="addselection": | |
event = { | |
"type": "addselection", | |
'userid': event["userid"], | |
'userlabel': event["userlabel"], | |
'usercolor': event["usercolor"], | |
} | |
websockets.broadcast(connected, json.dumps(event)) | |
elif event["command"]["type"]=="insert": | |
event = { | |
"type": "insert", | |
'index': event["index"], | |
'text': event["text"], | |
'userid': event["userid"], | |
'userlabel': event["userlabel"], | |
'usercolor': event["usercolor"], | |
'fileName':event["fileName"], | |
} | |
websockets.broadcast(connected, json.dumps(event)) | |
elif event["command"]["type"]=="replace": | |
event = { | |
"type": "replace", | |
'index': event["index"], | |
'length': event["length"], | |
'text': event["text"], | |
'userid': event["userid"], | |
'userlabel': event["userlabel"], | |
'usercolor': event["usercolor"], | |
'fileName':event["fileName"], | |
} | |
websockets.broadcast(connected, json.dumps(event)) | |
elif event["command"]["type"]=="edelelte": | |
event = { | |
"type": "edelelte", | |
'index': event["index"], | |
'length': event["length"], | |
'userid': event["userid"], | |
'userlabel': event["userlabel"], | |
'usercolor': event["usercolor"], | |
'fileName':event["fileName"], | |
} | |
websockets.broadcast(connected, json.dumps(event)) | |
elif event["command"]["type"]=="keep-alive": | |
event = { | |
"type": "keep-alive", | |
} | |
websockets.broadcast(connected, json.dumps(event)) | |
else: | |
# First player starts a new game. | |
pass | |
except Exception as exc: | |
# Send an "error" event if the move was illegal. | |
await error(websocket, str(exc)) | |
continue | |
break | |
except websockets.ConnectionClosed: | |
print("WebSocket connection closed") | |
break | |
except Exception as e: | |
print(f"Error in input thread: {str(e)}") | |
pass | |
async def process_input(user_input, process): | |
if process: | |
try: | |
if user_input == 'Ctrl+C': | |
print('Stopping process') | |
# Send Ctrl+C signal to the subprocess | |
process.send_signal(signal.SIGINT) | |
os.killpg(os.getpgid(process.pid), signal.SIGTERM) | |
# Read output and error asynchronously | |
# os.kill(int(process.pid), signal.CTRL_C_EVENT) | |
print('stoping signal sent') | |
else: | |
print(user_input) | |
process.stdin.write(user_input.encode('utf-8') + b'\n') | |
# process.stdin.flush() | |
except Exception as e: | |
print(f"Error writing to process stdin: {str(e)}") | |
else: | |
print("No process available to write to.") | |
async def create_file_structure(websocket, data, base_path='.'): | |
if data['type'] == 'folder': | |
folder_path = os.path.join(base_path, data['name']) | |
os.makedirs(folder_path, exist_ok=True) | |
for child in data['children']: | |
await create_file_structure(websocket,child, base_path=folder_path) | |
elif data['type'] == 'file': | |
mode = auto_detect_mode(data['content']) | |
file_path = os.path.join(base_path, data['name']) | |
with open(file_path,mode) as file: | |
file.write(data['content']) | |
event = { | |
"type": "msg", | |
"message": "project created", | |
} | |
await send_message_chunks(websocket,event) | |
async def error(websocket, message): | |
""" | |
Send an error message. | |
""" | |
event = { | |
"type": "error", | |
"message": message, | |
} | |
await send_message_chunks(websocket,event) | |
async def exe(websocket,connected,key,process_ids): | |
""" | |
Receive and process moves from a player. | |
""" | |
# print('in exe') | |
message_parts = {} | |
async for message in websocket: | |
# Parse a "play" event from the UI. | |
data = json.loads(message) | |
msg_id = data['msg_id'] | |
part = data['part'] | |
if msg_id not in message_parts: | |
message_parts[msg_id] = [] | |
message_parts[msg_id].append(part) | |
if data['end']: | |
full_message = ''.join(message_parts[msg_id]) | |
print(f"Received full message: {full_message}") | |
event = json.loads(full_message) | |
del message_parts[msg_id] | |
assert event["type"] == "cmd" | |
# command = event["command"] | |
print(event,2) | |
try: | |
if event["command"]["type"]=="shell": | |
base_path = os.path.join(os.getcwd(), 'projects', key,event["project_name"]) | |
directory_path = base_path | |
event_handler = FileHandler(websocket,connected) | |
observer = Observer() | |
observer.schedule(event_handler, path=directory_path, recursive=True) | |
observer.start() | |
try: | |
# await execute_command(websocket, key,event["project_name"], event["command"]["command"], connected) | |
# base_path = os.path.join(os.getcwd(), 'projects', key,event["project_name"]) | |
print(base_path) | |
mod_command = f'cd {base_path} && {event["command"]["command"]}' | |
print(mod_command) | |
try: | |
process = await asyncio.create_subprocess_shell( | |
mod_command, | |
# cwd=base_path, | |
stdin=asyncio.subprocess.PIPE, | |
stdout=asyncio.subprocess.PIPE, | |
stderr=asyncio.subprocess.PIPE, | |
# text=True, | |
) | |
id=process.pid | |
process_ids.append(id) | |
async def send_message(message): | |
# print('sending msg') | |
# await websocket.send(f'data: {message}') | |
websockets.broadcast(connected,json.dumps(message) ) | |
await asyncio.gather( | |
handle_user_input(websocket,key, process, connected,process_ids), | |
read_process_output(process, websocket) | |
) | |
return_code = await process.wait() | |
if return_code == 0: | |
event = { | |
"type": "terminal-data-end", | |
} | |
await send_message_chunks(websocket,event) | |
else: | |
event = { | |
"type": "terminal-data-end", | |
} | |
await send_message_chunks(websocket,event) | |
process_ids.remove(id) | |
except Exception as e: | |
await error(websocket, str(e)) | |
except KeyboardInterrupt: | |
pass # Handle KeyboardInterrupt to gracefully stop the observer | |
observer.stop() | |
observer.join() | |
elif event["command"]["type"]=="write": | |
await wirte_file(websocket, key,event["project_name"], event["path"], event["content"], connected) | |
elif event["command"]["type"] == "curl": | |
method = event.get("method", "GET") | |
url = event["url"] | |
body = event.get("data", "") | |
headers = event.get("headers", {}) | |
if method.upper() == "GET": | |
response = requests.get(url, headers=headers) | |
elif method.upper() == "POST": | |
response = requests.post(url, data=body, headers=headers) | |
else: | |
response = {"error": "Unsupported method"} | |
if isinstance(response, dict): | |
response_data = { | |
"type": "error", | |
"data": response, | |
} | |
await websocket.send(json.dumps(response_data)) | |
return | |
# Parse HTML content | |
souptmp = BeautifulSoup(response.text, "html.parser") | |
# Step 1: Define the initial HTML string | |
html_str = """ | |
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
</head> | |
<body> | |
<h1>Hello, World!</h1> | |
</body> | |
</html> | |
""" | |
# Step 2: Parse the initial HTML content | |
soup = BeautifulSoup(html_str, 'html.parser') | |
# Step 3: Replace the existing body content with the new content from souptmp | |
if soup.body: | |
soup.body.clear() | |
soup.body.append(souptmp) | |
else: | |
print("No <body> tag found in the HTML") | |
# Inline CSS and JavaScript | |
soup = inline_resources(soup, url) | |
response_data = { | |
"type": "web-data", | |
"data": str(soup), | |
} | |
# await websocket.send(json.dumps(response_data)) | |
await send_html_chunks(websocket, soup) | |
elif event["command"]["type"]=="create": | |
if event["item"]=="folder": | |
await create_folder(websocket, key,event["project_name"], event["path"],event["name"],event['root'],event['targetElementData'],event["rpath"], connected) | |
else: | |
await create_file(websocket, key,event["project_name"], event["path"],event["name"],event['root'],event['targetElementData'],event["rpath"], connected) | |
elif event["command"]["type"]=="delete": | |
await delete_item(websocket, key,event["project_name"], event["path"],event['rpath'],event['targetElementData'], connected) | |
elif event["command"]["type"]=="get_content": | |
await get_file_content(websocket, key,event["project_name"],event["filePath"], event["rfilePath"],event["fileName"] ,connected) | |
elif event["command"]["type"]=="rename": | |
await rename_item(websocket, key,event["project_name"], event["path"],event['rpath'], event["name"], event["root"], connected) | |
elif event["command"]["type"]=="join": | |
await join(websocket, event["join"]) | |
elif event["command"]["type"]=="sendDir": | |
data=json.loads(event["file_structure"]) | |
event = { | |
"type": "createDir", | |
"path": data, | |
"root":event['root'], | |
} | |
# websockets.broadcast(connected,json.dumps(event)) | |
websockets.broadcast(connected, json.dumps(event)) | |
elif event["command"]["type"]=="createItemUI": | |
event = { | |
"type": "createItemUI", | |
'targetElementData':event['targetElementData'], | |
'data':event['data'] | |
} | |
# websockets.broadcast(connected,json.dumps(event)) | |
websockets.broadcast(connected, json.dumps(event)) | |
elif event["command"]["type"]=="renameItemInUI": | |
event = { | |
"type": "renameItemInUI", | |
'path':event['path'], | |
'new_path':event['new_path'], | |
'name':event['name'], | |
'new_rpath':event['new_rpath'], | |
} | |
# websockets.broadcast(connected,json.dumps(event)) | |
websockets.broadcast(connected, json.dumps(event)) | |
elif event["command"]["type"]=="createFolderUI": | |
event = { | |
"type": "createFolderUI", | |
'targetElementData':event['targetElementData'], | |
'data':event['data'] | |
} | |
# websockets.broadcast(connected,json.dumps(event)) | |
websockets.broadcast(connected, json.dumps(event)) | |
elif event["command"]["type"]=="removeItemFromUI": | |
event = { | |
"type": "removeItemFromUI", | |
'targetElementData':event['targetElementData'], | |
'path':event['path'] | |
} | |
# websockets.broadcast(connected,json.dumps(event)) | |
websockets.broadcast(connected, json.dumps(event)) | |
elif event["command"]["type"]=="project": | |
base_path = os.path.join(os.getcwd(), 'projects',key, event["project_name"]) | |
data=json.loads(event["file_structure"]) | |
await create_file_structure(websocket,data, base_path=base_path) | |
# await rename_item(websocket, key,event["project_name"], event["path"], event["name"], connected) | |
elif event["command"]["type"]=="collabration": | |
event = { | |
"type": "collabration", | |
'name': event["name"], | |
'line': event["cursorPos-line"], | |
'ch': event["cursorPos-ch"], | |
'file':event["file"], | |
'content': event["content"], | |
'color':event["color"] | |
} | |
websockets.broadcast(connected, json.dumps(event)) | |
elif event["command"]["type"]=="cursor": | |
event = { | |
"type": "cursor", | |
'offset': event["offset"], | |
'userid': event["userid"], | |
'userlabel': event["userlabel"], | |
'usercolor': event["usercolor"], | |
'fileName':event["fileName"], | |
} | |
websockets.broadcast(connected, json.dumps(event)) | |
elif event["command"]["type"]=="select": | |
event = { | |
"type": "select", | |
'id': event["id"], | |
'startOffset': event["startOffset"], | |
'endOffset': event["endOffset"], | |
'userid': event["userid"], | |
'userlabel': event["userlabel"], | |
'usercolor': event["usercolor"], | |
'fileName':event["fileName"], | |
} | |
websockets.broadcast(connected, json.dumps(event)) | |
elif event["command"]["type"]=="addselection": | |
event = { | |
"type": "addselection", | |
'userid': event["userid"], | |
'userlabel': event["userlabel"], | |
'usercolor': event["usercolor"], | |
} | |
websockets.broadcast(connected, json.dumps(event)) | |
elif event["command"]["type"]=="insert": | |
event = { | |
"type": "insert", | |
'index': event["index"], | |
'text': event["text"], | |
'userid': event["userid"], | |
'userlabel': event["userlabel"], | |
'usercolor': event["usercolor"], | |
'fileName':event["fileName"], | |
'tabStatus':event['tabStatus'], | |
} | |
websockets.broadcast(connected, json.dumps(event)) | |
elif event["command"]["type"]=="replace": | |
event = { | |
"type": "replace", | |
'index': event["index"], | |
'length': event["length"], | |
'text': event["text"], | |
'userid': event["userid"], | |
'userlabel': event["userlabel"], | |
'usercolor': event["usercolor"], | |
'fileName':event["fileName"], | |
'tabStatus':event['tabStatus'], | |
} | |
websockets.broadcast(connected, json.dumps(event)) | |
elif event["command"]["type"]=="edelelte": | |
event = { | |
"type": "edelelte", | |
'index': event["index"], | |
'length': event["length"], | |
'userid': event["userid"], | |
'userlabel': event["userlabel"], | |
'usercolor': event["usercolor"], | |
'fileName':event["fileName"], | |
} | |
websockets.broadcast(connected, json.dumps(event)) | |
elif event["command"]["type"]=="keep-alive": | |
event = { | |
"type": "keep-alive", | |
} | |
else: | |
# First player starts a new game. | |
pass | |
except Exception as exc: | |
# Send an "error" event if the move was illegal. | |
await error(websocket, str(exc)) | |
continue | |
async def start(websocket,events): | |
user = User() | |
connected = {websocket} | |
process_ids = [] | |
join_key = secrets.token_urlsafe(12) | |
JOIN[join_key] = user, connected | |
try: | |
# Send the secret access tokens to the browser of the first player, | |
# where they'll be used for building "join" and "watch" links. | |
event = { | |
"type": "init", | |
"join": join_key, | |
} | |
await send_message_chunks(websocket,event) | |
await exe(websocket,connected,join_key,process_ids) | |
finally: | |
del JOIN[join_key] | |
project_path = os.path.join(os.getcwd(), 'projects', JOIN[join_key]) | |
shutil.rmtree(project_path) | |
for id in process_ids: | |
os.killpg(os.getpgid(id), signal.SIGTERM) | |
async def join(websocket, key): | |
""" | |
Handle a connection from the second player: join an existing game. | |
""" | |
# Find the Connect Four game. | |
try: | |
user, connected = JOIN[key] | |
except KeyError: | |
await error(websocket, "collabration not found.") | |
return | |
# Register to receive moves from this game. | |
connected.add(websocket) | |
try: | |
event = { | |
"type": "sendDir", | |
} | |
websockets.broadcast(connected,json.dumps(event)) | |
await exe(websocket,connected,key) | |
finally: | |
connected.remove(websocket) | |
async def handler(websocket): | |
""" | |
Handle a connection and dispatch it according to who is connecting. | |
""" | |
# Receive and parse the "init" event from the UI. | |
message_parts = {} | |
async for message in websocket: | |
data = json.loads(message) | |
msg_id = data['msg_id'] | |
part = data['part'] | |
if msg_id not in message_parts: | |
message_parts[msg_id] = [] | |
message_parts[msg_id].append(part) | |
if data['end']: | |
full_message = ''.join(message_parts[msg_id]) | |
print(f"Received full message: {full_message}") | |
event = json.loads(full_message) | |
if event["type"] == "init": | |
if "join" in event: | |
# Second player joins an existing game. | |
await join(websocket, event["join"]) | |
else: | |
# First player starts a new game. | |
await start(websocket, full_message) | |
elif event["type"] == "cmd": | |
# print('executing commad') | |
# Execute a command in the project folder. | |
await execute_command(websocket, event["project_name"], event["command"]) | |
async def main(): | |
# Set the stop condition when receiving SIGTERM. | |
loop = asyncio.get_running_loop() | |
stop = loop.create_future() | |
loop.add_signal_handler(signal.SIGTERM, stop.set_result, None) | |
port = int(os.environ.get("PORT", "7860")) | |
async with websockets.serve(handler, "0.0.0.0", port): | |
await stop | |
if __name__ == "__main__": | |
asyncio.run(main()) |