diff --git a/README.md b/README.md new file mode 100644 index 0000000..5501c03 --- /dev/null +++ b/README.md @@ -0,0 +1,78 @@ +# Project Name + +This project provides an interactive development environment with a focus on real-time file exploration and management within a playground. It includes a backend server for handling socket connections and file operations, and a frontend for displaying and interacting with the file system. + +## Features + +- **Real-time File Explorer**: Browse and manage project files within a containerized environment. +- **File Content Editing**: Read and write file content directly from the frontend. +- **Playground Management**: Create and manage isolated development playgrounds. + +## Getting Started + +### Prerequisites + +- Node.js (v14 or higher) +- Docker and Docker Compose + +### Installation + +1. **Clone the repository**: + + ```bash + git clone + cd project-name + ``` + +2. **Install backend dependencies**: + + ```bash + cd server + npm install + ``` + +3. **Install frontend dependencies**: + + ```bash + cd ../frontend + npm install + ``` + +### Running the Application + +1. **Start the backend server**: + + ```bash + cd server + npm start + ``` + +2. **Start the frontend development server**: + + ```bash + cd ../frontend + npm start + ``` + +3. Open your browser and navigate to `http://localhost:3000` (or the port specified by your frontend). + +## Architecture Overview + +The application consists of two main parts: + +- **`server/`**: Node.js backend handling API requests, WebSocket communication, and orchestration of playground containers. It establishes a socket connection to a `file_server` running inside each playground container to proxy file system requests. +- **`frontend/`**: React application providing the user interface, including the code editor and file explorer. It communicates with the backend via WebSockets to send file system requests and receive updates. + +## Key Components + +- **File Explorer Listener (`server/src/socket/events/onFileExplorer.ts`)**: Manages the socket connection between the main server and the `file_server` within a playground container, relaying file system events. +- **File Explorer Frontend Component (`frontend/src/modules/playground/components/Explorer/FileExplorer.tsx`)**: Renders the directory tree and handles user interactions (e.g., opening files, requesting directory updates). +- **Source Code Context (`frontend/src/modules/playground/utils/SourceCodeContext/SourceCodeContext.tsx`)**: Manages the state of open files, active file, and whether the file explorer is connected, providing an interface for components to interact with file data. +- **File Server (`server/playground_images/vite-react/proxy_container/file_server/src/server.ts`)**: A service running inside the playground container responsible for file system operations (read, write, directory listing). + +## API Endpoints (relevant for playground management) + +- `/api/playground/create`: Creates and starts a new playground instance. +- `/api/playgrounds`: Lists active playgrounds. + +See `server/src/routes/playground.route.ts` for details. diff --git a/docs/api/file-management.md b/docs/api/file-management.md new file mode 100644 index 0000000..a9ddb79 --- /dev/null +++ b/docs/api/file-management.md @@ -0,0 +1,118 @@ +# File Management API + +This document describes the WebSocket-based API for interacting with the file system within a playground environment. These operations are facilitated by the File Explorer system. + +All file system requests from the frontend are proxied through the main backend server to a dedicated `file_server` running inside the target playground container. + +## WebSocket Events for File Management + +### `file_server_request` (Client to Server) + +This event is emitted by the frontend client to request a file system operation. The server then forwards this request to the `file_server` within the specific playground. + +**Structure:** + +`socket.emit('file_server_request', '', ...);` + +**Operations:** + +#### `get_directory` + +Requests the directory listing (tree structure) of a specified path. + +- **Arguments:** + - `path` (string): The path to the directory to list (e.g., `'/'`, `'/src'`). + +- **Example Frontend Usage:** + ```typescript + socket.emit('file_server_request', 'get_directory', '/'); + ``` + +- **Backend (File Server) Handling:** + ```typescript + // server/playground_images/vite-react/proxy_container/file_server/src/server.ts + io.on('connection', (socket) => { + socket.on('get_directory', (directoryPath) => { + const directoryTree = getDirectoryTree(directoryPath); + emitSocket('file_server_response', { type: 'directory_tree', data: directoryTree }); + }); + }); + ``` + +#### `get_file_content` + +Requests the content of a specific file. + +- **Arguments:** + - `path` (string): The path to the file (e.g., `'/src/App.tsx'`). + +- **Example Frontend Usage:** + ```typescript + socket.emit('file_server_request', 'get_file_content', '/src/index.js'); + ``` + +- **Backend (File Server) Handling:** + ```typescript + // server/playground_images/vite-react/proxy_container/file_server/src/server.ts + socket.on('get_file_content', (filePath) => { + const fileData = getFileContent(filePath); + emitSocket('file_server_response', { type: 'file_content', data: fileData }); + }); + ``` + +#### `write_file_content` + +Writes new content to a specific file (or creates it if it doesn't exist). + +- **Arguments:** + - `path` (string): The path to the file. + - `content` (string): The new content to write to the file. + +- **Example Frontend Usage:** + ```typescript + const newContent = 'console.log("Hello, playground!");'; + socket.emit('file_server_request', 'write_file_content', '/src/main.js', newContent); + ``` + +- **Backend (File Server) Handling:** + ```typescript + // server/playground_images/vite-react/proxy_container/file_server/src/server.ts + socket.on('write_file_content', (filePath, content) => { + const result = writeToFile(filePath, content); + emitSocket('file_server_response', { type: 'write_file_result', data: result }); + }); + ``` + +### `file_server` (Server to Client) + +This event is emitted by the main backend server to send responses or updates from the `file_server` in the playground back to the frontend client. + +**Structure:** + +`socket.on('file_server', (data) => { ... });` + +**Data Payload (examples):** + +- **Connection Status:** + ```json + { "connected": true } + ``` + Sent when the `FileExplorerListener` successfully connects to the playground's `file_server`. + +- **Directory Tree Update:** + ```json + { "type": "directory_tree", "data": { /* JSON tree structure */ } } + ``` + Response to a `get_directory` request. + +- **File Content:** + ```json + { "type": "file_content", "data": { "path": "/src/App.tsx", "content": "...", "fileExists": true } } + ``` + Response to a `get_file_content` request. + +- **Write File Result:** + ```json + { "type": "write_file_result", "data": { "success": true, "path": "/src/main.js" } } + ``` + Response to a `write_file_content` request. diff --git a/docs/guides/file-explorer.md b/docs/guides/file-explorer.md new file mode 100644 index 0000000..778c163 --- /dev/null +++ b/docs/guides/file-explorer.md @@ -0,0 +1,126 @@ +# File Explorer Guide + +This guide explains the architecture and functionality of the file explorer component, detailing how it enables real-time file system interaction within the development playgrounds. + +## Overview + +The file explorer is a core feature that allows users to browse directories, open files, and manage content directly from the frontend. It operates by establishing a dedicated WebSocket connection for file system events, proxying requests between the frontend and a `file_server` running inside each playground container. + +## Architecture + +Interaction with the file system involves several components: + +1. **Frontend (`frontend/src/modules/playground/components/Explorer/FileExplorer.tsx`)**: + - Displays the directory tree. + - Sends `file_server_request` events to the main backend server for operations like `get_directory`, `get_file_content`, and `write_file_content`. + - Listens for `file_server` events from the backend to update its state (e.g., directory tree, file content). + +2. **Main Backend Server (`server/src/socket/events/onFileExplorer.ts`)**: + - Contains the `FileExplorerListener` class, which is responsible for managing the connection to the `file_server` in the playground. + - Receives `file_server_request` events from the frontend and forwards them to the respective `file_server` in the playground container via its own WebSocket connection. + - Relays `file_server_response` events from the playground's `file_server` back to the frontend. + + ```typescript + class FileExplorerListener { + clientSocket: Socket; + fileServerSocket: ClientSocket; + + directoryServicePort: any; + playgroundId: any; + + constructor(clientSocket, directoryServicePort, playgroundId) { + this.clientSocket = clientSocket; + this.directoryServicePort = directoryServicePort; + this.playgroundId = playgroundId; + } + + connectFileExplorerSocket() { + this.fileServerSocket = connect(`http://host.docker.internal:${this.directoryServicePort}`); + this.fileServerSocket.on('connect', () => { + this.clientSocket.emit('file_server', { connected: true }); + }); + } + + init() { + this.connectFileExplorerSocket(); + this.clientSocket.on('file_server_request', (event, ...args) => { + this.fileServerSocket.emit(event, ...args); + }); + this.fileServerSocket.on('file_server_response', (data) => { + this.clientSocket.emit('file_server', data); + }); + } + } + ``` + +3. **File Server within Playground Container (`server/playground_images/vite-react/proxy_container/file_server/src/server.ts`)**: + - A dedicated service running inside each playground container. + - Handles actual file system operations (reading files, writing files, listing directories using the `tree` command). + - Emits `file_server_response` events back to the main backend server with the results of its operations. + + ```typescript + const getFileContent = (path) => { + const fileExists = existsSync(path); + if (fileExists) { + try { + const content = readFileSync(path, 'utf-8'); + return { path, content: content, fileExists: true }; + } catch (error) {} + } + return { path, content: '', fileExists: false }; + }; + + const writeToFile = (path, content) => { + try { + writeFileSync(path, content, 'utf-8'); + return { success: true, path }; + } catch (error) { + return { success: false, path, error: error.message }; + } + }; + ``` + +## Frontend Interaction Examples + +### Connecting to the File Explorer + +The `FileExplorer` component uses the `useSocketContext` and `useCodeFilesContext` to manage its connection status and file data. + +```typescript +const FileExplorer = () => { + const { socket } = useSocketContext(); + const { + addFile, + setActiveFile, + updateFile, + files: codeFiles, + activeFile, + setExplorerConnected, + } = useCodeFilesContext(); + + // ... useEffect for socket event listeners +}; +``` + +### Requesting Directory Content + +To get the content of a directory, the frontend emits a `file_server_request` event: + +```typescript +const getDirectory = (path: string) => { + socket.emit('file_server_request', 'get_directory', path); +}; +``` + +### Updating File Content + +When a user edits a file, the `updateFileData` function (from `SourceCodeContext`) propagates the change: + +```typescript +const updateFileData = (path: string, content: string) => { + if (!socket || !socket.connected) { + return; + } + socket.emit('file_server_request', 'write_file_content', path, content); +}; +```